VR应用由于其特殊性,帧数是一个十分关键的部分。如果帧数不够高,那么体验VR的人很容易就会感到头晕。研究显示,VR应用要想达到较好的效果,并且不会让人在体验时感到生理上的不适,那么至少要达到90fps以上的帧率。所以对于我们的VR应用来说,做好优化是十分关键的一步,这不同于制作其他非VR应用。
这里列出几项对我们的项目有帮助的优化技巧。
静态批处理
在我们的场景中将会存在大量的静态模型物体,比如墙面,地面,桌椅,柱子等。这些物体在运行过程中将不会移动,那么我们可以将这些不移动的物体标记为static,那么Unity在构建场景时将会自动进行烘焙,从而减少DrawCall的次数,一定程度上提高帧数。
使用贴图集
有于每一个不同的材质会引发一次DrawCall,为了减少DrawCall的次数,我们需要减少场景中的材质数量。我们使用不同材质的原因往往是这些材质需要不同的贴图,那么只要这些材质使用的Shader是相同的话,我们就可以使用贴图集来为这些不同的贴图创建相同的材质。贴图集中包含了许多小的贴图,我们可以在材质中使用不同的Offset来指定不同的贴图。
使用Pro Draw call Optimizer Light来生成贴图集(这个是免费版本)。
使用异步场景切换
当我们需要从一个房间移动到另一个房间时,可以把它们放到不同的场景中,在移动时使用Application.LoadLevelAsync()
来异步加载场景,这样可以节省内存资源。
使用LightProbe
为了使动态物体在烘培好的场景中受到比较好的光照效果,我们可以使用LightProbe来给场景中可能移动的物体添加光照效果。
脚本优化
使用事件来代替Update的逐帧检测,尽量避免使用Update方法,可以一定程度提高性能。
缓存一些资源文件,来避免在每次调用的时候动态加载。
private static GameObject _prefab;
private static GameObject prefab
{
get
{
if (_prefab == null) {
_prefab = Resources.Load<GameObject>("OverlayUI/Hand");
}
return _prefab;
}
}
使用自己编写的MonoBase
类来代替MonoBehaviour
类,其中使用协程和委托来代替原invoke
方法,提高易用性和安全性。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public abstract class MonoBase : MonoBehaviour {
public void Invoke(ThreadStart task,float delay) {
StartCoroutine(startInvoke(task, delay));
}
private Dictionary<string, bool> _invokeRepeatingIds;
private Dictionary<string, bool> invokeRepeatingIds
{
get
{
if (_invokeRepeatingIds == null) {
_invokeRepeatingIds = new Dictionary<string, bool>();
}
return _invokeRepeatingIds;
}
}
public void InvokeRepeating(ThreadStart task, string id, float rate) {
if (invokeRepeatingIds.ContainsKey(id)) {
throw new Exception("start a repeat invoke with reduplicate id '" + id + "'.");
}
invokeRepeatingIds.Add(id, true);
StartCoroutine(startInvokeRepeating(task, id, rate));
}
public void CancelInvokeRepeating(string id) {
if (invokeRepeatingIds.ContainsKey(id)) {
invokeRepeatingIds[id] = false;
}else {
throw new Exception("repeat invoke with id '" + id + "' not found.");
}
}
private IEnumerator startInvoke(ThreadStart task,float delay) {
yield return new WaitForSeconds(delay);
task();
}
private IEnumerator startInvokeRepeating(ThreadStart task, string id, float rate) {
while (invokeRepeatingIds[id]) {
task();
yield return new WaitForSeconds(rate);
}
invokeRepeatingIds.Remove(id);
}
}