UPL5-11——禁用未使用的脚本和对象

本章代码关键字

1
2
3
4
5
OnBecameVisible() { }                        // 当依附对象存在Renderer且对任一摄像机(主摄像机、UI摄像机、小地图摄像机、Scene 视图摄像机等)可见时调用
OnBecameInvisible() { } // 当依附对象存在Renderer且对任何摄像机(主摄像机、UI摄像机、小地图摄像机、Scene 视图摄像机等)都不可见时调用
GeometryUtility.CalculateFrustumPlanes() // 获取摄像机视锥体六个平面
GeometryUtility.TestPlanesAABB() // 判断一个AABB包围盒是否与一组平面(如视锥体)相交或包含
renderer.bounds // 获取渲染器的包围盒

禁用未使用的脚本和对象

所谓的禁用未使用的脚本和对象,是指在 特定游戏类型中 对那些不在玩家视野内,或者离玩家太远的游戏对象,采用不处理他们的优化方案,达到节约性能的目的
尤其是项目内容庞大,场景复杂或者设备性能有限的情况下,这种优化手段非常实用

注意:这种优化方案,只适用于那些禁用对象不会对游戏过程产生明显影响的游戏

Unity 的视锥剔除和遮挡剔除

  • 视锥剔除:

    自动剔除摄像机视野之外的对象
    只影响渲染系统,不会绘制被剔除的对象

  • 遮挡剔除:

    剔除虽然在摄像机视野内、但被其他物体挡住的对象
    同样是只影响渲染系统,防止浪费 GPU 绘制不可见的物体

    关于遮挡剔除,详细可见:UPL9-4——遮挡剔除

也就是说,Unity 中的视锥剔除和遮挡剔除其实只会优化 GPU 渲染层面的性能
对象身上依附的各种组件,还是会照常执行,并不能起到禁用未使用的脚本和对象 优化 CPU 性能的目的

OnBecameVisible 和 OnBecameInvisible 函数

  • OnBecameVisible()​:在渲染器(Renderer)对 任一摄像机(主摄像机、UI摄像机、小地图摄像机、Scene 视图摄像机等) 可见时调用
  • OnBecameInvisible()​:在渲染器(Renderer)对 任何摄像机(主摄像机、UI摄像机、小地图摄像机、Scene 视图摄像机等) 都不可见时调用
1
2
3
4
5
6
7
8
9
private void OnBecameVisible()
{
Debug.Log("GameObject OnBecameVisible");
}

private void OnBecameInvisible()
{
Debug.Log("GameObject OnBecameInvisible");
}

注意:

  1. Scene 视图摄像机也会导致调用该函数
  2. 所依附的对象上必须有 Renderer 渲染器相关组件
  3. 对象失活后,由于渲染器也会失活,这两个函数不再响应
  4. 即使这两个函数所在的组件失活,只要渲染器存在,就会响应

利用距离禁用对象

通过判断需要禁用的对象和玩家或摄像机距离过远时主动禁用对象
比如我们通过怪物、子弹等等管理器,不停判断他们和主要目标的距离
当超过需求值时主动禁用对象

1
2
3
4
5
if (Vector3.Distance(this.transform.position, otherTrans.position) >= 50f)
{
// 禁用相关的逻辑
// 比如失活组件、对象失活、放入缓存池等等
}

注意:在进行距离判断时,可以采用距离的平方(Vector3.sqrMagnitude)进行比较,这样可以减少开方的消耗

1
2
3
4
5
if ((this.transform.position - otherTrans.position).sqrMagnitude >= 2500)
{
// 禁用相关的逻辑
// 比如失活组件、对象失活、放入缓存池等等
}

主动判断视锥范围

Unity 中提供了一个几何工具类,其中有两个重要方法:

  1. GeometryUtility.TestPlanesAABB

    用于判断一个AABB包围盒是否与一组平面(如视锥体)相交或包含

  2. GeometryUtility.CalculateFrustumPlanes

    用于获取摄像机视锥体六个平面

而通过渲染器可以得到该对象的包围盒:renderer.bounds

常用于:

  1. 自定义检测
  2. 自定义剔除
  3. 镜头内检测
  4. 优化游戏对象激活与逻辑执行

等等

注意:场景中少量对象,可以放心使用该方法判断,若场景中对象过多,因采用分帧处理等等优化手段降低开销

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private Plane[] _planes;
private Renderer _renderer;

private void Start()
{
// 1. 获取摄像机视锥体平面
_planes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
_renderer = GetComponent<Renderer>();
}

private void Update()
{
// 2. 利用摄像机视锥体平面和对象的包围盒进行相交判断
if (GeometryUtility.TestPlanesAABB(_planes, _renderer.bounds))
{
Debug.Log("GameObject 在Main摄像机视锥内");
}
else
{
Debug.Log("GameObject 在Main摄像机视锥外");
}
}