Unity优化技巧:自动隐藏视野外的3D模型
在3D游戏开发中,场景中可能包含大量模型,而许多模型可能同时处于玩家视野之外。这些不可见的模型仍然消耗CPU和GPU资源,导致性能下降。本文将介绍如何实现一个自动隐藏视野外模型的系统,通过两个Unity脚本实现性能优化。
系统概述
这个系统由两个核心脚本组成:
- ModelVisibilityManager1:管理器脚本,负责跟踪所有需要自动隐藏的模型
- AutoHideWhenOutOfView1:组件脚本,附加到需要自动隐藏的模型上
系统工作流程:
- 管理器每帧检查所有注册模型的可见性
- 当模型离开相机视野时自动隐藏
- 当模型重新进入视野时自动显示
- 避免不必要的渲染和计算开销
脚本1:ModelVisibilityManager1(管理器)
using System.Collections.Generic;
using UnityEngine;public class ModelVisibilityManager1 : MonoBehaviour
{public static ModelVisibilityManager1 Instance { get; private set; }private List<AutoHideWhenOutOfView1> registeredControllers = new List<AutoHideWhenOutOfView1>();public Camera targetCamera;void Awake(){// 单例模式实现if (Instance == null){Instance = this;DontDestroyOnLoad(gameObject);}else{Destroy(gameObject);}// 设置默认相机if (targetCamera == null){targetCamera = Camera.main;}}// 注册模型控制器public void Register(AutoHideWhenOutOfView1 controller){if (!registeredControllers.Contains(controller)){registeredControllers.Add(controller);}}// 注销模型控制器public void Unregister(AutoHideWhenOutOfView1 controller){if (registeredControllers.Contains(controller)){registeredControllers.Remove(controller);}}void Update(){// 每帧检查所有注册模型的可见性foreach (var controller in registeredControllers){controller.ManualVisibilityCheck(targetCamera);}}
}
单例模式实现
public static ModelVisibilityManager1 Instance { get; private set; }
使用静态属性确保全局只有一个实例,其他脚本可以通过ModelVisibilityManager1.Instance
访问它。
初始化设置
void Awake()
{if (Instance == null){Instance = this;DontDestroyOnLoad(gameObject);}else{Destroy(gameObject);}if (targetCamera == null){targetCamera = Camera.main;}
}
- 如果当前实例为空,则设置当前实例为单例,并标记为在场景切换时不销毁
- 如果已存在实例,则销毁当前对象,保证单例的唯一性
- 如果
targetCamera
未被赋值,则使用场景中的主相机作为目标相机
注册管理
private List<AutoHideWhenOutOfView1> registeredControllers = new List<AutoHideWhenOutOfView1>();public void Register(AutoHideWhenOutOfView1 controller)
{if (!registeredControllers.Contains(controller)){registeredControllers.Add(controller);}
}public void Unregister(AutoHideWhenOutOfView1 controller)
{if (registeredControllers.Contains(controller)){registeredControllers.Remove(controller);}
}
- 维护一个列表存储所有需要自动隐藏的模型组件
- 提供
Register
和Unregister
方法管理组件列表
可见性检查
public void CheckVisibility(Camera camera)
{foreach (var controller in registeredControllers){controller.CheckVisibility(camera);}
}
- 遍历所有已注册的控制器
- 调用每个控制器的
CheckVisibility
方法进行可见性检测关键功能解析:
单例模式:
- 确保场景中只有一个管理器实例
- 使用
DontDestroyOnLoad
保持管理器在场景切换时不被销毁
相机设置:
- 默认使用场景主相机
- 可通过Inspector自定义目标相机
模型注册系统:
Register
方法添加需要自动隐藏的模型Unregister
方法移除已销毁的模型- 避免重复注册
每帧可见性检查:
- 在Update中遍历所有注册的模型
- 调用每个模型的可见性检查方法
脚本2:AutoHideWhenOutOfView1(模型组件)
using UnityEngine;[RequireComponent(typeof(Renderer))]
public class AutoHideWhenOutOfView1 : MonoBehaviour
{private Renderer modelRenderer;private bool isVisible = true;void Awake(){// 获取渲染器组件modelRenderer = GetComponent<Renderer>();if (modelRenderer == null){enabled = false; // 无渲染器则禁用脚本}// 向管理器注册自身ModelVisibilityManager1.Instance.Register(this);}void OnEnable(){isVisible = true;}// 由管理器调用的可见性检查方法public void ManualVisibilityCheck(Camera camera){bool isInView = IsInCameraView(camera);// 从不可见到可见:激活模型if (!isVisible && isInView){gameObject.SetActive(true);isVisible = true;}// 从可见到不可见:隐藏模型else if (isVisible && !isInView){gameObject.SetActive(false);isVisible = false;}}// 检查模型是否在相机视野内bool IsInCameraView(Camera camera){Bounds bounds = modelRenderer.bounds;Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);return GeometryUtility.TestPlanesAABB(planes, bounds);}void OnDestroy(){// 从管理器中注销ModelVisibilityManager1.Instance.Unregister(this);}
}
组件初始化
private bool isVisible = true;
private Renderer modelRenderer;void Awake()
{modelRenderer = GetComponent<Renderer>();
}
isVisible
:记录当前模型是否处于激活状态- 获取模型上的渲染器组件用于后续检测
可见性检测逻辑
public void CheckVisibility(Camera camera)
{bool isInView = IsInCameraView(camera);if (!isVisible && isInView){gameObject.SetActive(true);isVisible = true;//Debug.Log(name + " 已重新进入视野,已激活");}else if (isVisible && !isInView){gameObject.SetActive(false);isVisible = false;//Debug.Log(name + " 离开视野,已隐藏");}
}
- 调用
IsInCameraView
检测模型是否在相机视野内 - 当模型从不可见变为可见时激活模型
- 当模型从可见变为不可见时停用模型
视锥体检测算法
private bool IsInCameraView(Camera camera)
{Bounds bounds = modelRenderer.bounds;Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);return GeometryUtility.TestPlanesAABB(planes, bounds);
}
- 获取模型的包围盒(AABB)
- 计算相机的视锥体平面
- 使用Unity内置方法检测包围盒是否与视锥体相交
清理注册
void OnDestroy()
{ModelVisibilityManager1.Instance.Unregister(this);
}
- 当对象被销毁时,从管理器的注册列表中移除自身
关键功能解析:
渲染器依赖:
- 使用
[RequireComponent]
确保模型有渲染器 - 无渲染器时自动禁用脚本
- 使用
可见性状态管理:
isVisible
变量跟踪当前可见状态- 避免重复激活/停用模型
视野检测算法:
- 使用
GeometryUtility
类计算相机视锥体 - 通过包围盒(AABB)与视锥体平面相交测试判断可见性
- 高效且精确的3D空间检测方法
- 使用
生命周期管理:
Awake
中注册到管理器OnDestroy
中从管理器注销- 确保没有内存泄漏