3.7.2 算法优化
思路是找出最耗CPU的算法或逻辑,优化之。
- 空间换时间。利用预排序/预处理/缓存/动态规划等等思路换取CPU的性能。
- 选取更快的算法。属于数据结构和算法的范畴,思路是将O(n2)降低成O(n)或O(logn),具体可以参看《算法导论》《游戏编程算法与技巧》《游戏核心算法编程内幕》等书籍。
3.7.3 脚本优化
通常引擎底层是用C++等Native语言实现,而脚本用动态语言(Java/C#/Lua/Python)实现,它们中间隔着一层厚重的模拟器或封装层。Unity引擎与C#之间的关系如下图:
Unity在打包游戏时会通过Mono将C#代码生成IL中间语言,如果是iOS平台,还会通过IL2CPP生成C++代码。简单点说,Unity引擎核心和C#等脚本语言的交互要通过Mono厚重的中间层。由此产生了额外的开销,导致脚本语言运行效率低下。可以通过以下一些方法降低脚本的开销:
- 删除脚本内的空回调。即便脚本对象的回调函数为空,但也会产生引擎核心与脚本层的开销。
- 脚本对象如果引用其他对象,可以在初始化时缓存。
class Tester
{
private Object _obj = null;
void init()
{
_obj = FindObject("MyObjectName"); // 初始化时先找到物体。
}
void update()
{
if (_obj)
{
_obj.update(); // 帧内直接访问。
}
}
}
- 帧更新/循环语句内避免产生堆的临时对象。可以将临时对象移至循环语句外,或声明成类的成员,在初始化时赋值。
- 利用可见性回调。在可见性回调内做禁止/恢复比较耗时的操作。
- 字符串接很容易引起临时对象,需警惕。可采用更高效的拼接方式,如C#的StringBuilder。
3.7.4 条件测试
条件测试主要用于耗时的调用优化,将每帧必然更新的操作,加入各种条件检查,以减少耗时操作的概率。
void Update()
{
DoCalculation(); // 耗时操作
}
// 改成:
void Update()
{
// 加入各种条件测试
if (_dirty && _visible && _moved && _timeInterval>0.1)
{
DoCalculation();
}
}
3.7.5 避免重复
游戏一般涉及的模块众多,角色状态机复杂,触发事件多且杂,往往会在同一帧内多次调用同一个耗时API,引发额外的开销。可以通过条件测试,时间间隔,Log输出,调用栈调试等方法解决这个问题。