1. 尽量避免每帧处理
比方:
function Update() { DoSomeThing(); }
可改为每5帧处理一次:
function Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } }
2. 定时反复处理用 InvokeRepeating 函数实现
比方。启动0.5秒后每隔1秒运行一次 DoSomeThing 函数:
function Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }
3. 优化 Update, FixedUpdate, LateUpdate 等每帧处理的函数
函数里面的变量尽量在头部声明。
比方:
function Update() { var pos: Vector3 = transform.position; }
可改为
private var pos: Vector3; function Update(){ pos = transform.position; }
4. 主动回收垃圾
给某个 GameObject 绑上下面的代码:
function Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
5. 执行时尽量降低 Tris 和 Draw Calls
预览的时候,可点开 Stats,查看图形渲染的开销情况。
特别注意 Tris 和 Draw Calls 这两个參数。
一般来说,要做到:
Tris 保持在 7.5k 下面
Draw Calls 保持在 20 下面
6. 压缩 Mesh
导入 3D 模型之后,在不影响显示效果的前提下,最好打开 Mesh Compression。
Off, Low, Medium, High 这几个选项,可酌情选取。
7. 避免大量使用 Unity 自带的 Sphere 等内建 Mesh
Unity 内建的 Mesh,多边形的数量比較大,假设物体不要求特别圆滑。可导入其它的简单3D模型取代。
8. 优化数学计算
比方。假设能够避免使用浮点型(float),尽量使用整形(int),尽量少用复杂的数学函数比方 Sin 和 Cos 等等
降低固定增量时间
将固定增量时间值设定在0.04-0.067区间(即,每秒15-25帧)。您能够通过Edit->Project Settings->Time来改变这个值。这样做减少了FixedUpdate函数被调用的频率以及物理引擎运行碰撞检測与刚体更新的频率。假设您使用了较低的固定增量时间,而且在主角身上使用了刚体部件。那么您能够启用插值办法来平滑刚体组件。
降低GetComponent的调用
使用 GetComponent或内置组件訪问器会产生明显的开销。您能够通过一次获取组件的引用来避免开销,并将该引用分配给一个变量(有时称为"缓存"的引用)。
比如。假设您使用例如以下的代码:
function Update () {
transform.Translate(0, 1, 0);
}
通过以下的更改您将获得更好的性能:
var myTransform : Transform;
function Awake () {
myTransform = transform;
}
function Update () {
myTransform.Translate(0, 1, 0);
}
避免分配内存
您应该避免分配新对象。除非你真的须要。由于他们不再在使用时,会添加垃圾回收系统的开销。
您能够常常反复使用数组和其它对象,而不是分配新的数组或对象。
这样做优点则是尽量降低垃圾的回收工作。同一时候,在某些可能的情况下,您也能够使用结构(struct)来取代类(class)。这是由于,结构变量主要存放在栈区而非堆区。
由于栈的分配较快,而且不调用垃圾回收操作,所以当结构变量比較小时能够提升程序的执行性能。
可是当结构体较大时。尽管它仍可避免分配/回收的开销。而它由于"传值"操作也会导致单独的开销,实际上它可能比等效对象类的效率还要低。
最小化GUI
使用GUILayout 函数能够非常方便地将GUI元素进行自己主动布局。然而,这样的自己主动化自然也附带着一定的处理开销。您能够通过手动的GUI功能布局来避免这样的开销。
此外,您也能够设置一个脚本的useGUILayout变量为 false来全然禁用GUI布局:
function Awake () {
useGUILayout = false;
}
使用iOS脚本调用优化功能
UnityEngine 命名空间中的函数的大多数是在 C/c + +中实现的。从Mono的脚本调用 C/C++函数也存在着一定的性能开销。您能够使用iOS脚本调用优化功能(菜单:Edit->Project Settings->Player)让每帧节省1-4毫秒。此设置的选项有:
Slow and Safe – Mono内部默认的处理异常的调用
Fast and Exceptions Unsupported –一个高速运行的Mono内部调用。只是。它并不支持异常,因此应慎重使用。它对于不须要显式地处理异常(也不须要对异常进行处理)的应用程序来说,是一个理想的候选项。
优化垃圾回收
如上文所述,您应该尽量避免分配操作。
可是,考虑到它们是不能全然杜绝的,所以我们提供两种方法来让您尽量降低它们在游戏执行时的使用:
假设堆比較小。则进行高速而频繁的垃圾回收
这一策略比較适合执行时间较长的游戏,当中帧率是否平滑过渡是基本的考虑因素。像这种游戏一般会频繁地分配小块内存,但这些小块内存仅仅是临时地被使用。
假设在iOS系统上使用该策略,那么一个典型的堆大小是大约 200 KB。这样在iPhone 3G设备上。垃圾回收操作将耗时大约 5毫秒。假设堆大小添加到1 MB时,该回收操作将耗时大约 7ms。因此。在普通帧的间隔期进行垃圾回收有时候是一个不错的选择。通常,这种做法会让回收操作执行的更加频繁(有些回收操作并非严格必须进行的),但它们能够高速处理而且对游戏的影响非常小:
if (Time.frameCount % 30 == 0)
{
System.GC.Collect();
}
可是。您应该小心地使用这样的技术,而且通过检查Profiler来确保这样的操作确实能够减少您游戏的垃圾回收时间
假设堆比較大,则进行缓慢且不频繁的垃圾回收
这一策略适合于那些内存分配 (和回收)相对不频繁,而且能够在游戏停顿期间进行处理的游戏。假设堆足够大,但还没有大到被系统关掉的话。这样的方法是比較适用的。可是,Mono执行时会尽可能地避免堆的自己主动扩大。因此,您须要通过在启动过程中预分配一些空间来手动扩展堆(ie,你实例化一个纯粹影响内存管理器分配的"无用"对象):
function Start() {
var tmp = new System.Object[1024];
// make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks
for (var i : int = 0; i < 1024; i++)
tmp[i] = new byte[1024];
// release reference
tmp = null;
}
游戏中的暂停是用来对堆内存进行回收。而一个足够大的堆应该不会在游戏的暂停与暂停之间被全然占满。所以。当这样的游戏暂停发生时。您能够显式请求一次垃圾回收:
System.GC.Collect();
另外,您应该慎重地使用这一策略并时刻关注Profiler的统计结果,而不是假定它已经达到了您想要的效果。