当前位置: 首页 > news >正文

Unity优化技巧:自动隐藏视野外的3D模型

在3D游戏开发中,场景中可能包含大量模型,而许多模型可能同时处于玩家视野之外。这些不可见的模型仍然消耗CPU和GPU资源,导致性能下降。本文将介绍如何实现一个自动隐藏视野外模型的系统,通过两个Unity脚本实现性能优化。

系统概述

这个系统由两个核心脚本组成:

  1. ModelVisibilityManager1:管理器脚本,负责跟踪所有需要自动隐藏的模型
  2. 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);}
}
  • 维护一个列表存储所有需要自动隐藏的模型组件
  • 提供RegisterUnregister方法管理组件列表

可见性检查

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);
}
  • 当对象被销毁时,从管理器的注册列表中移除自身

关键功能解析:

  1. 渲染器依赖

    • 使用[RequireComponent]确保模型有渲染器
    • 无渲染器时自动禁用脚本
  2. 可见性状态管理

    • isVisible变量跟踪当前可见状态
    • 避免重复激活/停用模型
  3. 视野检测算法

    • 使用GeometryUtility类计算相机视锥体
    • 通过包围盒(AABB)与视锥体平面相交测试判断可见性
    • 高效且精确的3D空间检测方法
  4. 生命周期管理

    • Awake中注册到管理器
    • OnDestroy中从管理器注销
    • 确保没有内存泄漏
http://www.dtcms.com/a/310421.html

相关文章:

  • 【人工智能-16】机器学习:概念、工具介绍、数据集、特征工程
  • 铁皮矫平机进阶小百科
  • C# _Json数据
  • MySQL 45 讲 18-20
  • React 19 革命性升级:编译器自动优化,告别手动性能调优时代
  • 携程PMO资深经理、携程技术委员会人工智能委员会秘书陈强受邀为PMO大会主持人
  • 开源vGPU解决方案HAMi
  • 2025.8.1
  • python中appium 的NoSuchElementException错误 原因以及解决办法
  • C++基础语法
  • Redis实战(5)-- 高级数据结构 HyperLogLog
  • 调整Idea缓存目录,释放C盘空间
  • UniApp与WebView双向通信机制及生产级实现方案全解析
  • 振动波形转音频播放并做声纹聚类
  • 【数据分享】南海综合波浪数据(1945-2018 年)(获取方式看文末)
  • 【历史人物】【王安石】简历与生平
  • win11怎么看本机ip地址?怎么查看代理端口?
  • SAP Datasphere 03 - 数据权限
  • OpenShift AI - 用 Hardware profiles 为运行环境分配可用的硬件规格
  • 什么是股指期货的不对冲策略?
  • 【Flutter】内存泄漏总结
  • HarmonyOS 应用拉起系列(一):应用与元服务互通方式
  • 哔哩哔哩Android视频编辑页的架构升级
  • 二进制数本身没有默认的有符号或无符号解释
  • Eden 和 Survivor 比例可以调整么,参数是什么?还用到了哪些参数?
  • 数新智能 CTO 原攀峰:DataCyber 面向 AI 时代的多模态数据湖设计与实践
  • MYSQL:JDBC编程
  • C语言笔记4:错题整理
  • QT开发---图形与图像(补充)
  • ZYNQ与MPSoC实战:从Vitis入门到项目开发