前言:
对于GC,大家可能不陌生把,也就是内存回收。同时笔者在做自己的小游戏中发现很多细节都会影响GC,现在就给大家梳理下一些需要注意的地方。
进入主题:
在说CPU优化时,谈起GC是不是觉得很奇怪?其实笔者不这么觉得,虽然GC是用来处理内存回收的,但是却增加了CPU的开销。因此对于GC的优化目标就是尽量少的触发GC。
首先我们要知道所谓的GC是Mono运行时的机制,而非Unity3D游戏引擎的机制,所以GC也主要是针对Mono的对象来说的,而它管理的也是Mono的托管堆。 明白了这一点,你也就明白了GC不是用来处理引擎的Assets(贴图,音效,模型等等)的内存释放的,因为U3D引擎也有自己的内存堆而不是和Mono一起使用所谓的托管堆。
其次我们还要清楚什么东西会被分配到托管堆上?对,就是引用类型。引用类型包括:用户自定义的类,接口,委托,数组,字符串,Object.而值类型包括:几种基本数据类型(如:int,float,bool等),结构体,枚举,空类型。所以我们我们就应该尽量不要区使用那些引用类型。
那么GC什么时候会触发呢?两种情况:
因此为了达到优化CPU的目的,我们就不能频繁的触发GC。而上文也说了GC处理的是托管堆,而不是Unity3D引擎的那些资源,其实说白了GC的优化也就是代码的优化。一下有事隔热你总结有些是参考了网上写的不错的文章。具体需要注意的地方如下:
注意:
(1) 类在传递的时候,传递的内容是位于托管内存中的位置,结构体在传递的时候,传递的内容是位于程序堆栈区的内容。当类的传递对象修改时,将同时修改源对象,而结构体的传递对象修改时,不会对源对象产生影响。
(2)在一个类中,可以定义默认的、不带参数的构造函数,而在结构体中不能定义默认的、不带参数的构造函数。两者都可以定义带有参数的构造函数,通过这些参数给各自的字段赋值或初始化。
(3)类支持继承类,结构体不支持结构体但是可以继承接口。
(4)继承的基类不同,但都是从object派生的 。
(5)类成员在没有修饰符情况下默认私有(我记得Java好像是default,那是我还被老师提问来着,呵呵!),结构体默认public。详情如下
(如果一个类的成员没有任何权限修饰,那么它门就是缺省包访问权限,也就是同一个 包内其它类可以访问,但包外就不可以。对于同一个文件夹下的、没有用package的classes,Java会自动将这些classes初见为隶属于该目录的default下)。
7.尽量不要再Update函数中做复杂计算,如有需要,可以隔N帧计算一次,比如:
void Update(){ if(Time.frameCount%8==0) { DoSomething(); }}
8.在使用数组或ArrayList对象时应当注意,缓存其长度:
length=myArray.Length;
9.定时重复调用可以使用InvokeRepeating函数实现,比如,启动1秒后每隔1秒执行一次 DoSomeThing 函数:
void Start()
{
InvokeRepeating("DoSomeThing", 0.5f, 1.0f);
}
10.少使用临时变量,特别是在Update OnGUI等实时调用的函数中。
void Update()
{
Vector3 post;
post=transform.position;
}
可改为:
private Vector3 post;
void Update()
{
post=transform.position;
}
12.尽量减少函数调用栈,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x).
13.不要使用SendMessage之类的方法,他比直接调用方法慢了100倍,你可以直接调用或通过C#的委托来实现。
14.使用javascript或Boo语言时,你最好确定变量的类型,不要使用动态类型,这样会降低效率,你可以在脚本开头使用#pragmastrict 来检查,这样当你编译你的游戏时就不会出现莫名其妙的错误了。比如:
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
function Start ()
{
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}
这里我们强制foo为指定类型,你将获得更好的性能。
15.优化数学运算,尽量避免使用float,而使用int,特别是在手机游戏中,尽量少用复杂的数学函数,比如sin,cos等函数。改除法/为乘法,例如:使用x*0.5f而不是 x/2.0f 。
16.压缩 Mesh 。导入 3D 模型之后,在不影响显示效果的前提下,最好开 Mesh Compression。 Off, Low, Medium, High 这几个选项,可酌情选取。对于单个Mesh最好使用一个材质。
17.删除空的Update方法。当通过Assets目录创建新的脚本时,脚本里会包括一个Update方法,当你不使用时删除它。
18.使用内置数组,内置数组是非常快的。ArrayList或Array类很容易使用,你能轻易添加元件。但是他们有完全不同的速度。 内置数组有固定长度,并且大多时候你会事先知道最大长度然后填充它。内置数组最好的一点是他们直接嵌入结构数据类型在一个紧密的缓存里,而不需要任何额外 类型信息或其他开销。因此,在缓存中遍历它是非常容易的,因为每个元素都是对齐的。比如: Vector3.zero;
19.不要使用原生的GUI方法,用NGUI代替最好!
20.需要隐藏/显示或实例化来回切换的对象,尽量不要使用SetActiveRecursively或active,而使用将对象远远移出相机范围和移回原位的做法或者使用OnBecameVisible()和OnBecameVisible();
21.只在一个脚本中使用OnGUI方法;
22.对于方法的参数的优化:善于使用ref关键字。值类型的参数,是通过将实参的值复制到形参,来实现按值传递到方法,也就是我们通常说的按值传递。复制嘛,总会让人感觉很笨重。比如Matrix4x4这样比较复杂的值类型,如果直接复制一份新的,反而不如将值类型的引用传递给方法作为参数。
..........
最后的最后:
感觉CPU的GC Script的优化方面会很多,暂时先暂时现总结这么点,之后我会不断补充的,如有更好的优化方面,可以在下面留言哦,谢谢咯。