String是一个很常用的引用类型对象。当代码里存在字符串拼接、直接或间接调用ToString()函数时,会生成字符串的副本,也就产生了内存分配。例如:调用Object.name属性,即使每次返回值是固定的,依然是不同的String对象,因为这里每次返回都是一个对象拷贝。所以建议可以通过把这类字符串预先缓存,或者在打包时生成一个名字的列表作为静态数据,提供给运行时的逻辑直接读取。
部分Unity内置API在被调用时,都是返回对象拷贝。例如:Getcomponents、Sprite.Vertices、Input.Touches等。从设计角度是考虑代码安全性,防止外部直接去修改真正的对象数据。所以,这些属性返回值要做缓存。或者通过其他API来实现需求从而规避掉这个问题。
ParticleSystem API在Unity 2017.2之前的版本中,Stop和Simulate内部实现使用了闭包。粒子系统的一些API,例如:Start、Stop、Pause、Clear、Simulate在调用它们时会递归调用当前粒子节点下面的所有子级节点,并会触发GetComponent,这带来了一定的CPU开销。如果需要调这几个方法的时候,函数参数withChildren可以设为false,不触发遍历子节点。在粒子对象初始化时,预存子节点,在需要时直接根据缓存的子节点列表分别调用它们的Start。
Canvas都建议做动静分离,频繁改动的元素和固定不变的元素分开到不同的Canvas。需要注意Canvas数量,数量多少根据UI的复杂程度、动静分离的Canvas个数进行测试,评估多少个Canvas是合理的。目前发现Unity2017.3中,出现过当Canvas数量达到十几个或更多时,带来的开销反而比不分拆时还大。
当我们需要手动释放一些对象的内存时,会有很多种方式,Unity提供了很多卸载各种资源的函数。主动调GC.collect是不必要的,如果一个对象的引用不是Null时,是不可能释放它的。GC只需要做好对象引用的清理就可以,剩下的还是由GC机制自动管理更好。我们可以通过自定义内存池和资源管理器,来很精细的控制每一种资源的生命周期。
AssetBundle压缩格式一般使用LZ4,但要注意AssetBundle的合理Unload时机。而LZMA格式,由于存在加载时解压后重压缩为LZ4的开销,一般情况下不建议使用。主Bundle卸载时,与它关联的依赖Bundle一定要根据引用计数来控制是否可以卸载,否则依赖Bundle的Asset容易引发内存泄露。
启用静态批处理将有助于性能的提升。在《Cubiques立体迷宫》中大多数的场景游戏对象都是静态的,这意味着唯一真正运动的物体主要是立方体角色。要启用静态批处理,只需将每个对象都设置为静态的,这是每个游戏对象都具有的属性,并且可以通过检视窗口进行设置。还要确保运行设置中在渲染部分启用静态批处理。
Rect Mask 2D是一种UI组件,与Mask组件类似。不同的是前者可以在UI元素超出遮罩区域时禁用这个元素,从而避免多余的渲染工作。
另一种UI组件Scroll Rect是用来显示大量内容的,它会隐藏部分内容并允许用户进行滚动操作。这种组件的交互可以给玩家带来更佳体验,但尽量少用,因为它对移动设别并不是特别友好。可以将其与Rect Mask 2D组件配合使用,以获得更佳性能。
参考:unity官方论坛