maybe I need grow more

本文探讨了Unity5.x中的内存优化和AssetBundle(AB)机制,包括图形学和渲染优化、AB资源加载与卸载、C#闭包和匿名函数的区别。重点介绍了AB的LZ4压缩、WWW加载方式以及内存管理策略,同时涉及lua与C#的交互。文章还提到了内存优化技巧,如减少字符串拼接、缓存组件和避免闭包的过度使用,以及流畅度优化策略,如减少MeshCollider使用和优化光照方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 成长,也许从来都不是一件容易的事儿。


  • 内存优化
  • 图形学、渲染优化
  • AssetBundle资源加载和卸载  
  • C#高级特性 (闭包和匿名函数的区别)

闭包和匿名函数的区别:

解释1: 闭包是指有权访问另一个函数作用域中的变量的函数。

解释2:闭包是一个函数和该函数被定义时的词法环境的组合,一个函数的返回值是一个函数引用,外部调用这个函数的引用时,需要堆栈中保留函数执行的变量环境,这种关系就叫做闭包。

匿名函数,一般用到匿名函数的时候都是立即执行的。通常叫做自执行匿名函数或者自调用匿名函数。常用来构建沙箱模式,作用是开辟封闭的变量作用域环境,

闭包跟函数是否匿名没有直接关系,匿名函数和具名函数都可以创建闭包。

 

Unity5.XAB机制

http://www.cnblogs.com/murongxiaopifu/p/5629415.html#autoid-3-3-0

 

ChunkBasedCompression 新增LZ4进行压缩,加载时不需要加载整个AB,只加载对应数据块儿。

 

新增Manifest清单文件记录AB依赖关系,不必要再需要人为的处理PopAssetDependencies/PushAssetDependencis

 

AB压缩格式:

Lzma 默认的ab压缩格式,压缩比很高,但是加载时需要整个加载ab文件,会影响运行时加载速度,形成较大的内存开销。

不压缩,不压缩的AB加载速度快,但是体积大。

Lz4时5.3版本之后新增的压缩选项,压缩比没有lzma高,但优点在于加载时不需要整个加载AB,因此在加载速度和内存上有较大优势。

 

随包的AB建议lz4压缩,兼顾文件大小和加载速度,网络更新的ab建议lzma压缩,高压缩比可以减少数据流量。

 

www(url)的加载方式,加载完成后会在内存中创建较大的webStream,通常为原AB大小的3-5倍,便于后续加载直接从内存加载。

www.loadFromCacheOrDownload()改方法的优势在于能将解压缩之后的ab文件存入磁盘作为缓存,只会在内存中创建较小的serilizedFile。而后的AssetBundle.loadAsset直接通过IO从磁盘缓存中获取。

 

AssetBundle.loadFromFile 与老版本CreatFromFile机制上的区别在于,之前版本只支持从解压后的AB文件创建资源,新版本对于无压缩或者LZ4方式的AB文件可以直接创建AssetBundle对象。

 

UWA关于AssetBundle的建议:

1/ 对于需要常驻内存的Bundle文件来说,优先考虑减小内存占用,因此对于存放非Prefab资源(特别是纹理)的Bundle文件,可以考虑使用WWW.LoadFromCacheOrDownload或AssetBundle.CreateFromFile加载,从而避免WebStream常驻内存;

2/ 而对于存放较多Prefab资源的Bundle,则考虑使用new WWW加载,因为这类Bundle用WWW.LoadFromCacheOrDownload加载时产生的SerializedFile可能会比new WWW产生的WebStream更大。

3/ 对于加载完后即卸载的Bundle文件,则分两种情况:优先考虑速度(加载场景时)和优先考虑流畅度(游戏进行时)。

1)加载场景的情况下,需要注意的是避免WWW对象的逐个加载导致的CPU空闲,可以考虑使用加载速度较快的WWW.LoadFromCacheOrDownload或AssetBundle.CreateFromFile,但需要避免后续大量地进行Load资源的操作,引起IO开销(可以尝试直接LoadAll)。

2) 游戏进行的情况下,则需要避免使用同步操作引起卡顿,因此可以考虑使用new WWW配合AssetBundle.LoadAsync来进行平滑的资源加载,但需要注意的是,对于Shader、较大的Texture等资源,其初始化操作通常很耗时,容易引起卡顿,因此建议将这类资源在加载场景时进行预加载。

只在Bundle需要加密的情况下,考虑使用CreateFromMemory,因为该接口加载速度较慢。

尽量避免在游戏进行中调用Resources.UnloadUnusedAssets(),因为该接口开销较大,容易引起卡顿,可尝试使用Resources.Unload(obj)来逐个进行卸载,以保证游戏的流畅度。

 

AB加载卸载的操作流:

 

各种加载方式的内存、性能对比图:


内存优化&流畅度(fps)优化

代码GC和卡顿:

字符串拼接、

循环内部New对象、

反射减损、FieldInfo/MethodInfo缓存

循环for/foreach/iterator、foreach效率最差

装箱拆箱,

缓存所需的动态MonoBehaviour组件,或者在频繁instantiate/destroy应考虑pooling

异步waitfor进行缓存

不再运行时大量使用闭包(闭包效率?)

尽可能重用容器,以及容器操作的合理性(dic/list扩容,移除顺序)

不要大量使用delegate的-=/+=,

当你的对象订阅了外部的事件,而又没有取消订阅,那么该对象是不会被GC回收的!这会造成很恐怖的问题,产生了几千万个对象没法被回收。而且匿名函数无法取消订阅,就算是两个代码格式完全一样的匿名函数,编译器生成的也是两个不同的方法签名。链接:http://www.cnblogs.com/buptzym/archive/2013/03/15/2962300.html

减少不必要的 shader varients

关于代码GC的原理和优化思路:

https://www.cnblogs.com/zblade/p/6445578.html


内存优化:

如果有较大的assetBundle,不要使用lzma格式,使用lz4

确保AssetBundle里没有重复的资源(AB依赖处理)

确保资源正确加载和卸载

不需要RW的texture&mesh关闭RW

不需要的texture关闭mipmap

模型顶点的normals和tangents,应该在导入设置或playersetting里去除

确保大多数纹理使用压缩格式(后处理工具相关)

确保贴图尺寸合理、确保mesh面数合理

AnimationClip的分情况加载


流畅度优化:

减少meshColider的使用

适当情况下使用static batching/dynamic batching/自定义 batching

UI图集的使用,动静态分离,避免材质穿插、

使用合理的光照方案

使用合理的后处理

合理的骨骼数

合理的半透明物体占屏比例,避免过多OverDraw

避免使用Unity自带的地形系统

 


机型适配策略及品质控制

画面质量控制:

**基本品质控制

实时阴影品质、骨骼动画质量、纹理质量、垂直同步

**角色品质控制

根据和玩家主角的距离改变角色的渲染,如:是否开启描边、角色阴影模式实时/投影片儿/关闭、根据距离控制动画更新频率

**其他

分辨率整体缩放、根据对象layer控制渲染距离、根据配置切换抗锯齿类型、后处理开关:FXAA(快速抗锯齿/bloom/调色/镜头精神)

角色渲染优化:

**角色渲染合并

****纹理合并(通过相机拍摄RT合并、通过获取TextureRawData合并、通过GraphicsCopy合并)

        支持不同平台下支持不同的纹理压缩格式,shader实际上对不同类型图的精度要求不同。

****Mesh合并

        Unity的Mesh合并方法的问题:控制力不足、submesh合并会复制顶点,顶点数据翻倍

       自定义mesh合并:获取所有mesh顶点、法线等数据、重新拼接成一个mesh。

****重新绑定动画:skinMeshrender设置正确的bones、mesh有正确的BindPose--骨骼对应顶点信息。

        bindpose计算量巨大,使用ComputeShader将计算量推给GPU。

        bindPose预先烘培mesh数据

        部分不适合做合并的角色组件不参与合并。

总归是在内存和DrawCall中寻找平衡。

 场景画面品质控制

****在较低配置上减少细节的渲染

****减少难以观察到的细节渲染

****在不同的品质上真的做出性能差异

****在低端机上也能有更好的画面

****在高端机上将性能集中到更有意义的地方

具体手段:植被分组,赋予不同优先级,场景特效根据重要性设置不同layer,决定特效细节的展示分级、氛围特效控制,设置相机最远渲染距离。


 lua和c#交互

c#和lua交互是怎么实现的?

从最早的lua纯反射调用C# ---> 纯C#实现的lua虚拟机 ---->luajit+c#静态lua导出方案

猜想1:lua虚拟机有lua堆栈的完全控制权,lua调用C#需提供C#函数的桥接代码,并将其注册到lua虚拟机中,即压入lua栈。

c#调lua,首先lua虚拟机从lua栈中获取函数信息,然后在lua编译器在Global Table查找luafunction,并运行返回结果,将结果压入lua栈,宿主语言可以访问lua栈中的内存数据,返回给c#。

所以最重要的是搞清楚lua虚拟机工作原理和lua堆栈访问。

tips:如何在lua调用中体现c#语言中的高级特性,取决于lua框架(xlua、tolua)的实现?

热更框架如何实现hotfixed?

在需要热更的类或者方法上打上[hotfix]标签,然后在lua调c#的桥接代码中获取热更标识,如果出现标识则执行lua补丁代码,否则执行c#原生代码。

更舒服的开源方案InjectFix:

https://blog.youkuaiyun.com/tencent__open/article/details/100789546

https://github.com/Tencent/InjectFix

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值