第10章 Unity静态场景优化与导航系统:高效渲染与智能寻路的实践指南

10.1 光照烘焙贴图的配置与优化策略

10.1.1 光照烘焙贴图的配置与优化

在商业级游戏开发中,光照烘焙是优化静态场景渲染性能的关键技术。通过预计算光照信息并存储到纹理中,可以大幅减少实时渲染的计算负担。这一过程的数学基础是辐射度算法和全局光照方程,它们描述了光线在场景中的传播和相互作用。

从物理光学角度分析,光照烘焙的核心是求解渲染方程:

Lo(p, ωo) = Le(p, ωo) + ∫Ω fr(p, ωi, ωo) Li(p, ωi) (ωi·n) dωi

其中Lo是出射辐射度,Le是自发光辐射度,fr是双向反射分布函数,Li是入射辐射度。在烘焙过程中,这个积分方程通过蒙特卡洛方法或光子映射等算法进行预计算。

在商业项目中,我们通常需要处理大规模场景的光照烘焙。以下是一个完整的烘焙配置管理类:

using UnityEngine;
using System.Collections.Generic;
using System.IO;

namespace CommercialGame.LightingSystem
{
    /// <summary>
    /// 光照烘焙管理器 - 负责管理和执行光照烘焙流程
    /// </summary>
    public class LightmapBaker : MonoBehaviour
    {
        [SerializeField]
        private string lightmapDataPath = "Assets/LightmapData/";
        
        [SerializeField]
        private int lightmapResolution = 1024;
        
        [SerializeField]
        private float indirectIntensity = 1.0f;
        
        [SerializeField]
        private int lightmapPadding = 4;
        
        private Dictionary<string, LightmapData> cachedLightmapData;
        
        /// <summary>
        /// 初始化光照烘焙系统
        /// </summary>
        public void InitializeBakingSystem()
        {
            cachedLightmapData = new Dictionary<string, LightmapData>();
            EnsureDirectoryExists(lightmapDataPath);
            
            Debug.Log($"光照烘焙系统初始化完成,贴图分辨率:{lightmapResolution},路径:{lightmapDataPath}");
        }
        
        /// <summary>
        /// 执行场景光照烘焙
        /// </summary>
        /// <param name="sceneName">场景名称</param>
        public void BakeSceneLighting(string sceneName)
        {
            if (string.IsNullOrEmpty(sceneName))
            {
                Debug.LogError("场景名称不能为空");
                return;
            }
            
            // 设置光照贴图参数
            LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
            
            // 配置烘焙参数
            var bakeSettings = new LightmapBakeSettings
            {
                indirectIntensity = indirectIntensity,
                lightmapResolution = lightmapResolution,
                padding = lightmapPadding,
                textureCompression = true
            };
            
            // 开始异步烘焙
            StartCoroutine(BakeLightingAsync(sceneName, bakeSettings));
        }
        
        /// <summary>
        /// 异步烘焙光照
        /// </summary>
        private System.Collections.IEnumerator BakeLightingAsync(string sceneName, LightmapBakeSettings settings)
        {
            Debug.Log($"开始烘焙场景:{sceneName}");
            
            // 标记静态对象
            MarkStaticObjectsForBaking();
            
            // 执行烘焙
            Lightmapping.BakeAsync();
            
            // 等待烘焙完成
            while (Lightmapping.isRunning)
            {
                float progress = Lightmapping.progress;
                Debug.Log($"烘焙进度:{progress:P}");
                yield return new WaitForSeconds(0.5f);
            }
            
            // 保存烘焙结果
            SaveLightmapData(sceneName);
            
            Debug.Log($"场景 {sceneName} 光照烘焙完成");
        }
        
        /// <summary>
        /// 标记需要烘焙的静态对象
        /// </summary>
        private void MarkStaticObjectsForBaking()
        {
            var staticRenderers = FindObjectsOfType<Renderer>();
            
            foreach (var renderer in staticRenderers)
            {
                GameObject gameObject = renderer.gameObject;
                
                // 检查对象是否为静态
                if (gameObject.isStatic)
                {
                    // 确保有光照贴图UV
                    Unwrapping.GenerateSecondaryUVSet(renderer.sharedMesh);
                    
                    // 设置光照贴图索引
                    renderer.lightmapIndex = -1;
                    renderer.realtimeLightmapIndex = -1;
                }
            }
        }
        
        /// <summary>
        /// 保存光照贴图数据
        /// </summary>
        private void SaveLightmapData(string sceneName)
        {
            LightmapData[] lightmapArray = LightmapSettings.lightmaps;
            
            if (lightmapArray.Length == 0)
            {
                Debug.LogWarning("没有光照贴图数据可保存");
                return;
            }
            
            // 创建光照数据配置
            var lightmapConfig = new SceneLightmapConfig
            {
                SceneName = sceneName,
                LightmapCount = lightmapArray.Length,
                BakeTime = System.DateTime.Now.ToString("yyyyMMdd_HHmmss"),
                Settings = new LightmapSettings
                {
                    Resolution = lightmapResolution,
                    IndirectIntensity = indirectIntensity
                }
            };
            
            // 保存配置到文件
            string configPath = Path.Combine(lightmapDataPath, $"{sceneName}_config.json");
            string jsonData = JsonUtility.ToJson(lightmapConfig, true);
            File.WriteAllText(configPath, jsonData);
            
            Debug.Log($"光照贴图配置已保存:{configPath}");
        }
        
        /// <summary>
        /// 确保目录存在
        /// </summary>
        private void EnsureDirectoryExists(string path)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }
        
        /// <summary>
        /// 光照烘焙设置
        /// </summary>
        [System.Serializable]
        private struct LightmapBakeSettings
        {
            public float indirectIntensity;
            public int lightmapResolution;
            public int padding;
            public bool textureCompression;
        }
        
        /// <summary>
        /// 场景光照贴图配置
        /// </summary>
        [System.Serializable]
        private class SceneLightmapConfig
        {
            public string SceneName;
            public int LightmapCount;
            public string BakeTime;
            public LightmapSettings Settings;
        }
    }
}

10.1.2 混合光照模式的实现与优化

在商业游戏中,纯烘焙光照无法满足动态物体的需求,因此混合光照模式成为必备方案。混合光照的核心是实时阴影与烘焙阴影的融合,这涉及到阴影贴图的合成算法。

从数学角度看,混合光照需要解决两个核心问题:

  1. 实时阴影与烘焙阴影的权重分配
  2. 阴影边缘的平滑过渡

以下是一个混合光照管理器的实现:

using UnityEngine;
using UnityEngine.Rendering;

namespace CommercialGame.LightingSystem
{
    /// <summary>
    /// 混合光照管理器 - 处理实时光照与烘焙光照的混合
    /// </summary>
    public class HybridLightingManager : MonoBehaviour
    {
        [SerializeField]
        [Range(0.0f, 1.0f)]
        private float realtimeShadowStrength = 0.7f;
        
        [SerializeField]
        [Range(0.0f, 1.0f)]
        private float bakedShadowStrength = 0.3f;
        
        [SerializeField]
        private float shadowTransitionDistance = 10.0f;
        
        private Camera mainCamera;
        private Vector3[] cameraFrustumCorners;
        
        /// <summary>
        /// 初始化混合光照系统
        /// </summary>
        private void Start()
        {
            mainCamera = Camera.main;
            cameraFrustumCorners = new Vector3[4];
            
            // 配置混合光照参数
            ConfigureHybridLighting();
            
            Debug.Log("混合光照系统初始化完成");
        }
        
        /// <summary>
        /// 配置混合光照参数
        /// </summary>
        private void ConfigureHybridLighting()
        {
            // 设置阴影距离
            QualitySettings.shadowDistance = CalculateOptimalShadowDistance();
            
            // 配置混合阴影模式
            LightmapSettings.lightmapsMode = LightmapsMode.CombinedDirectional;
            
            // 设置阴影级联
            if (QualitySettings.shadows == ShadowQuality.All)
            {
                QualitySettings.shadowCascades = 4;
                QualitySettings.shadowCascade2Split = 0.33f;
                QualitySettings.shadowCascade4Split = new Vector3(0.067f, 0.2f, 0.467f);
            }
        }
        
        /// <summary>
        /// 计算最佳阴影距离
        /// </summary>
        private float CalculateOptimalShadowDistance()
        {
            if (mainCamera == null)
            {
                return 50.0f;
            }
            
            // 获取相机视锥体远平面四个角
            mainCamera.CalculateFrustumCorners(
                new Rect(0, 0, 1, 1),
                mainCamera.farClipPlane,
                Camera.MonoOrStereoscopicEye.Mono,
                cameraFrustumCorners
            );
            
            // 计算最大可视距离
            float maxDistance = 0.0f;
            for (int i = 0; i < 4; i++)
            {
                Vector3 worldCorner = mainCamera.transform.TransformVector(cameraFrustumCorners[i]);
                float distance = worldCorner.magnitude;
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                }
            }
            
            // 根据性能需求调整阴影距离
            float shadowDistance = Mathf.Min(maxDistance * 0.8f, 100.0f);
            
            return shadowDistance;
        }
        
        /// <summary>
        /// 更新每帧的混合光照参数
        /// </summary>
        private void Update()
        {
            UpdateDynamicShadowMixing();
            AdjustLightProbeBlending();
        }
        
        /// <summary>
        /// 更新动态阴影混合
        /// </summary>
        private void UpdateDynamicShadowMixing()
        {
            // 根据相机距离调整阴影混合权重
            float cameraDistance = Vector3.Distance(mainCamera.transform.position, transform.position);
            float distanceFactor = Mathf.Clamp01(cameraDistance / shadowTransitionDistance);
            
            // 动态调整阴影强度
            float dynamicRealtimeStrength = Mathf.Lerp(realtimeShadowStrength, 0.2f, distanceFactor);
            float dynamicBakedStrength = Mathf.Lerp(bakedShadowStrength, 0.8f, distanceFactor);
            
            // 应用阴影混合参数到所有灯光
            UpdateLightShadowMixing(dynamicRealtimeStrength, dynamicBakedStrength);
        }
        
        /// <summary>
        /// 更新灯光阴影混合参数
        /// </summary>
        private void UpdateLightShadowMixing(float realtimeStrength, float bakedStrength)
        {
            Light[] sceneLights = FindObjectsOfType<Light>();
            
            foreach (Light light in sceneLights)
            {
                if (light.type == LightType.Directional)
                {
                    // 定向光使用混合阴影
                    light.shadows = LightShadows.Soft;
                    light.shadowStrength = realtimeStrength;
                    
                    // 设置烘焙阴影强度
                    light.bakingOutput.lightmapBakeType = LightmapBakeType.Mixed;
                }
            }
        }
        
        /// <summary>
        /// 调整光照探针混合
        /// </summary>
        private void AdjustLightProbeBlending()
        {
            // 获取所有使用光照探针的渲染器
            Renderer[] dynamicRenderers = FindObjectsOfType<Renderer>();
            
            foreach (Renderer renderer in dynamicRenderers)
            {
                if (!renderer.gameObject.isStatic)
                {
                    // 动态物体使用光照探针
                    renderer.lightProbeUsage = LightProbeUsage.BlendProbes;
                    renderer.reflectionProbeUsage = ReflectionProbeUsage.BlendProbes;
                }
            }
        }
        
        /// <summary>
        /// 计算阴影混合的数学插值
        /// </summary>
        public float CalculateShadowBlendFactor(Vector3 position)
        {
            // 计算到最近静态物体的距离
            float distanceToStatic = CalculateDistanceToNearestStatic(position);
            
            // 使用平滑函数进行插值
            float blendFactor = SmoothStepBlend(distanceToStatic, shadowTransitionDistance);
            
            return blendFactor;
        }
        
        /// <summary>
        /// 计算到最近静态物体的距离
        /// </summary>
        private float CalculateDistanceToNearestStatic(Vector3 position)
        {
            float nearestDistance = float.MaxValue;
            
            // 在实际项目中,这里应该使用空间划分数据结构进行优化
            GameObject[] staticObjects = GameObject.FindGameObjectsWithTag("Static");
            
            foreach (GameObject staticObject in staticObjects)
            {
                float distance = Vector3.Distance(position, staticObject.transform.position);
                if (distance < nearestDistance)
                {
                    nearestDistance = distance;
                }
            }
            
            return nearestDistance;
        }
        
        /// <summary>
        /// 平滑步进插值函数
        /// </summary>
        private float SmoothStepBlend(float x, float maxDistance)
        {
            float t = Mathf.Clamp01(x / maxDistance);
            
            // 三次平滑插值:3t² - 2t³
            return t * t * (3.0f - 2.0f * t);
        }
    }
}

10.1.3 运行时动态切换烘焙贴图技术

在大型商业项目中,经常需要根据不同的时间、天气或游戏状态动态切换光照贴图。这需要高效的内存管理和快速的纹理切换机制。

以下实现了一个支持运行时切换光照贴图的系统:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace CommercialGame.LightingSystem
{
    /// <summary>
    /// 动态光照贴图切换器
    /// </summary>
    public class DynamicLightmapSwitcher : MonoBehaviour
    {
        [System.Serializable]
        public class LightmapSet
        {
            public string setName;
            public Texture2D[] lightmapFar;
            public Texture2D[] lightmapNear;
            public LightmapData[] lightmapData;
            public Color ambientLight;
            public Material skyboxMaterial;
        }
        
        [SerializeField]
        private LightmapSet[] lightmapSets;
        
        [SerializeField]
        private float transitionDuration = 2.0f;
        
        [SerializeField]
        private AnimationCurve transitionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
        
        private Dictionary<string, LightmapSet> lightmapSetDictionary;
        private LightmapSet currentLightmapSet;
        private Coroutine transitionCoroutine;
        
        /// <summary>
        /// 初始化光照贴图集
        /// </summary>
        private void Awake()
        {
            InitializeLightmapSets();
        }
        
        /// <summary>
        /// 初始化光照贴图集字典
        /// </summary>
        private void InitializeLightmapSets()
        {
            lightmapSetDictionary = new Dictionary<string, LightmapSet>();
            
            foreach (LightmapSet set in lightmapSets)
            {
                if (set.lightmapFar != null && set.lightmapNear != null)
                {
                    // 创建LightmapData数组
                    int lightmapCount = Mathf.Min(set.lightmapFar.Length, set.lightmapNear.Length);
                    set.lightmapData = new LightmapData[lightmapCount];
                    
                    for (int i = 0; i < lightmapCount; i++)
                    {
                        set.lightmapData[i] = new LightmapData
                        {
                            lightmapColor = set.lightmapFar[i],
                            lightmapDir = set.lightmapNear[i]
                        };
                    }
                    
                    lightmapSetDictionary[set.setName] = set;
                }
            }
        }
        
        /// <summary>
        /// 切换到指定的光照贴图集
        /// </summary>
        public void SwitchToLightmapSet(string setName, bool instant = false)
        {
            if (!lightmapSetDictionary.ContainsKey(setName))
            {
                Debug.LogError($"光照贴图集 '{setName}' 不存在");
                return;
            }
            
            LightmapSet targetSet = lightmapSetDictionary[setName];
            
            if (transitionCoroutine != null)
            {
                StopCoroutine(transitionCoroutine);
            }
            
            if (instant || transitionDuration <= 0)
            {
                ApplyLightmapSetImmediately(targetSet);
            }
            else
            {
                transitionCoroutine = StartCoroutine(TransitionToLightmapSet(targetSet));
            }
        }
        
        /// <summary>
        /// 立即应用光照贴图集
        /// </summary>
        private void ApplyLightmapSetImmediately(LightmapSet lightmapSet)
        {
            // 应用光照贴图
            LightmapSettings.lightmaps = lightmapSet.lightmapData;
            
            // 更新环境光照
            RenderSettings.ambientLight = lightmapSet.ambientLight;
            
            // 更新天空盒
            if (lightmapSet.skyboxMaterial != null)
            {
                RenderSettings.skybox = lightmapSet.skyboxMaterial;
            }
            
            // 更新所有静态渲染器的光照贴图索引
            UpdateAllRenderersLightmapIndices();
            
            currentLightmapSet = lightmapSet;
            Debug.Log($"已切换到光照贴图集: {lightmapSet.setName}");
        }
        
        /// <summary>
        /// 过渡到目标光照贴图集
        /// </summary>
        private IEnumerator TransitionToLightmapSet(LightmapSet targetSet)
        {
            LightmapSet startSet = currentLightmapSet;
            float elapsedTime = 0.0f;
            
            if (startSet == null)
            {
                ApplyLightmapSetImmediately(targetSet);
                yield break;
            }
            
            // 创建过渡用的中间光照贴图集
            LightmapSet intermediateSet = CreateIntermediateLightmapSet(startSet, targetSet);
            
            while (elapsedTime < transitionDuration)
            {
                float t = elapsedTime / transitionDuration;
                float curveValue = transitionCurve.Evaluate(t);
                
                // 插值环境光照
                RenderSettings.ambientLight = Color.Lerp(
                    startSet.ambientLight,
                    targetSet.ambientLight,
                    curveValue
                );
                
                // 插值光照贴图(实际项目中需要支持纹理混合)
                UpdateLightmapBlending(startSet, targetSet, curveValue);
                
                elapsedTime += Time.deltaTime;
                yield return null;
            }
            
            // 应用最终的光照贴图集
            ApplyLightmapSetImmediately(targetSet);
        }
        
        /// <summary>
        /// 创建中间光照贴图集
        /// </summary>
        private LightmapSet CreateIntermediateLightmapSet(LightmapSet start, LightmapSet end)
        {
            // 在实际商业项目中,这里需要实现真正的纹理混合
            // 本示例返回起始集作为占位符
            return start;
        }
        
        /// <summary>
        /// 更新光照贴图混合
        /// </summary>
        private void UpdateLightmapBlending(LightmapSet start, LightmapSet end, float blendFactor)
        {
            // 这是一个简化的实现
            // 在实际项目中,可能需要使用Shader或RenderTexture进行真正的纹理混合
            
            // 对于环境光立方体贴图,可以使用混合探针
            if (start.skyboxMaterial != null && end.skyboxMaterial != null)
            {
                // 在这里实现天空盒材质的插值逻辑
            }
        }
        
        /// <summary>
        /// 更新所有渲染器的光照贴图索引
        /// </summary>
        private void UpdateAllRenderersLightmapIndices()
        {
            Renderer[] allRenderers = FindObjectsOfType<Renderer>();
            
            foreach (Renderer renderer in allRenderers)
            {
                if (renderer.gameObject.isStatic)
                {
                    // 重置光照贴图索引,让Unity自动分配
                    renderer.lightmapIndex = -1;
                    renderer.realtimeLightmapIndex = -1;
                }
            }
            
            // 强制Unity重新分配光照贴图索引
            LightmapSettings.lightmaps = LightmapSettings.lightmaps;
        }
        
        /// <summary>
        /// 预加载光照贴图集
        /// </summary>
        public void PreloadLightmapSet(string setName)
        {
            if (lightmapSetDictionary.TryGetValue(setName, out LightmapSet lightmapSet))
            {
                StartCoroutine(PreloadLightmapTextures(lightmapSet));
            }
        }
        
        /// <summary>
        /// 预加载光照贴图纹理
        /// </summary>
        private IEnumerator PreloadLightmapTextures(LightmapSet lightmapSet)
        {
            foreach (Texture2D texture in lightmapSet.lightmapFar)
            {
                texture.wrapMode = TextureWrapMode.Clamp;
                texture.filterMode = FilterMode.Bilinear;
                texture.mipMapBias = -0.5f;
                
                // 触发纹理加载到GPU内存
                texture.GetNativeTexturePtr();
                
                yield return null;
            }
        }
    }
}

10.2 视觉遮挡剔除系统的实现原理

10.2.1 遮挡剔除算法的数学基础

遮挡剔除是渲染优化的核心技术,其数学基础主要涉及视锥体裁剪、层次深度缓冲(Hi-Z)和遮挡查询。在商业引擎中,通常使用层次ZBuffer算法,其时间复杂度为O(log n)。

关键算法包括:

  1. 保守性光栅化:确保不会错误剔除可见物体
  2. 深度范围估计:使用包围体层次结构加速测试
  3. 时间一致性:利用帧间连贯性减少计算量

以下是一个高级遮挡剔除系统的实现:

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

namespace CommercialGame.OcclusionSystem
{
    /// <summary>
    /// 高级遮挡剔除管理器
    /// </summary>
    public class AdvancedOcclusionCuller : MonoBehaviour
    {
        [SerializeField]
        private Camera occlusionCamera;
        
        [SerializeField]
        private LayerMask occluderLayer = -1;
        
        [SerializeField]
        private LayerMask occludeeLayer = -1;
        
        [SerializeField]
        private float occlusionTestRadius = 5.0f;
        
        [SerializeField]
        private int maxOcclusionQueriesPerFrame = 50;
        
        [SerializeField]
        private bool useAsyncOcclusionTests = true;
        
        private class OcclusionNode
        {
            public Renderer renderer;
            public Bounds worldBounds;
            public float lastVisibleTime;
            public bool isVisible;
            public float screenSize;
            public int queryId;
        }
        
        private Dictionary<Renderer, OcclusionNode> occlusionNodes;
        private Queue<int> availableQueryIds;
        private HashSet<int> pendingQueries;
        private ComputeBuffer occlusionBuffer;
        
        private static class OcclusionShaderIDs
        {
            public static readonly int WorldToOcclusionCamera = Shader.PropertyToID("_WorldToOcclusionCamera");
            public static readonly int OcclusionTestResults = Shader.PropertyToID("_OcclusionTestResults");
            public static readonly int ObjectBoundsBuffer = Shader.PropertyToID("_ObjectBoundsBuffer");
        }
        
        /// <summary>
        /// 初始化遮挡剔除系统
        /// </summary>
        private void Start()
        {
            InitializeOcclusionSystem();
            CreateOcclusionCamera();
        }
        
        /// <summary>
        /// 初始化遮挡系统数据结构
        /// </summary>
        private void InitializeOcclusionSystem()
        {
            occlusionNodes = new Dictionary<Renderer, OcclusionNode>();
            availableQueryIds = new Queue<int>();
            pendingQueries = new HashSet<int>();
            
            // 预分配查询ID
            for (int i = 0; i < 1024; i++)
            {
                availableQueryIds.Enqueue(i);
            }
            
            // 收集场景中所有可能的遮挡物和被遮挡物
            CollectOcclusionCandidates();
            
            Debug.Log($"遮挡系统初始化完成,找到 {occlusionNodes.Count} 个可遮挡对象");
        }
        
        /// <summary>
        /// 创建专用遮挡测试相机
        /// </summary>
        private void CreateOcclusionCamera()
        {
            if (occlusionCamera == null)
            {
                GameObject occlusionCameraObj = new GameObject("OcclusionCamera");
                occlusionCameraObj.transform.SetParent(transform);
                occlusionCamera = occlusionCameraObj.AddComponent<Camera>();
                
                // 配置遮挡相机参数
                occlusionCamera.cullingMask = occluderLayer;
                occlusionCamera.clearFlags = CameraClearFlags.Depth;
                occlusionCamera.depth = Camera.main.depth - 1;
                occlusionCamera.renderingPath = RenderingPath.Forward;
                occlusionCamera.allowMSAA = false;
                occlusionCamera.allowHDR = false;
                occlusionCamera.enabled = false;
            }
        }
        
        /// <summary>
        /// 收集遮挡候选对象
        /// </summary>
        private void CollectOcclusionCandidates()
        {
            Renderer[] allRenderers = FindObjectsOfType<Renderer>();
            
            foreach (Renderer renderer in allRenderers)
            {
                // 检查层级是否匹配
                int layerMask = 1 << renderer.gameObject.layer;
                if ((layerMask & occludeeLayer.value) == 0)
                {
                    continue;
                }
                
                var node = new OcclusionNode
                {
                    renderer = renderer,
                    worldBounds = renderer.bounds,
                    lastVisibleTime = Time.time,
                    isVisible = true,
                    screenSize = 0.0f,
                    queryId = -1
                };
                
                occlusionNodes[renderer] = node;
            }
        }
        
        /// <summary>
        /// 每帧更新遮挡测试
        /// </summary>
        private void Update()
        {
            UpdateCameraPosition();
            PerformOcclusionTests();
            UpdateVisibilityStates();
        }
        
        /// <summary>
        /// 更新遮挡相机位置
        /// </summary>
        private void UpdateCameraPosition()
        {
            if (Camera.main != null && occlusionCamera != null)
            {
                // 同步主相机参数
                occlusionCamera.transform.position = Camera.main.transform.position;
                occlusionCamera.transform.rotation = Camera.main.transform.rotation;
                occlusionCamera.fieldOfView = Camera.main.fieldOfView;
                occlusionCamera.nearClipPlane = Camera.main.nearClipPlane;
                occlusionCamera.farClipPlane = Camera.main.farClipPlane;
            }
        }
        
        /// <summary>
        /// 执行遮挡测试
        /// </summary>
        private void PerformOcclusionTests()
        {
            if (occlusionCamera == null)
            {
                return;
            }
            
            // 清空之前的查询结果
            CompletePendingQueries();
            
            // 按距离排序,优先测试近处的对象
            var sortedNodes = occlusionNodes.Values
                .OrderBy(node => Vector3.Distance(node.worldBounds.center, occlusionCamera.transform.position))
                .Take(maxOcclusionQueriesPerFrame);
            
            int testsThisFrame = 0;
            
            foreach (var node in sortedNodes)
            {
                if (testsThisFrame >= maxOcclusionQueriesPerFrame)
                {
                    break;
                }
                
                // 申请查询ID
                if (availableQueryIds.Count > 0)
                {
                    node.queryId = availableQueryIds.Dequeue();
                    pendingQueries.Add(node.queryId);
                    
                    // 执行异步遮挡查询
                    if (useAsyncOcclusionTests)
                    {
                        PerformAsyncOcclusionTest(node);
                    }
                    else
                    {
                        PerformImmediateOcclusionTest(node);
                    }
                    
                    testsThisFrame++;
                }
            }
        }
        
        /// <summary>
        /// 执行异步遮挡测试
        /// </summary>
        private void PerformAsyncOcclusionTest(OcclusionNode node)
        {
            // 使用GeometryUtility.TestPlanesAABB进行保守测试
            Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(occlusionCamera);
            
            if (!GeometryUtility.TestPlanesAABB(frustumPlanes, node.worldBounds))
            {
                // 在视锥体外,不可见
                node.isVisible = false;
                return;
            }
            
            // 计算屏幕空间大小
            Vector3 screenPoint = occlusionCamera.WorldToViewportPoint(node.worldBounds.center);
            node.screenSize = CalculateScreenSpaceSize(node.worldBounds);
            
            // 如果屏幕空间大小太小,可以跳过渲染
            if (node.screenSize < 0.001f)
            {
                node.isVisible = false;
                return;
            }
            
            // 使用物理系统进行精确遮挡测试
            RaycastHit[] hits = Physics.BoxCastAll(
                node.worldBounds.center,
                node.worldBounds.extents,
                occlusionCamera.transform.forward,
                Quaternion.identity,
                occlusionCamera.farClipPlane,
                occluderLayer
            );
            
            // 分析遮挡结果
            AnalyzeOcclusionResults(node, hits);
        }
        
        /// <summary>
        /// 执行立即遮挡测试
        /// </summary>
        private void PerformImmediateOcclusionTest(OcclusionNode node)
        {
            // 简单的视锥体测试
            if (IsInViewFrustum(node.worldBounds))
            {
                // 更精确的遮挡测试
                bool isOccluded = CheckPreciseOcclusion(node.worldBounds);
                node.isVisible = !isOccluded;
                
                if (node.isVisible)
                {
                    node.lastVisibleTime = Time.time;
                }
            }
            else
            {
                node.isVisible = false;
            }
        }
        
        /// <summary>
        /// 分析遮挡结果
        /// </summary>
        private void AnalyzeOcclusionResults(OcclusionNode node, RaycastHit[] hits)
        {
            if (hits.Length == 0)
            {
                node.isVisible = true;
                node.lastVisibleTime = Time.time;
                return;
            }
            
            // 检查是否有遮挡物在对象前面
            float objectDistance = Vector3.Distance(node.worldBounds.center, occlusionCamera.transform.position);
            bool isOccluded = false;
            
            foreach (var hit in hits)
            {
                float hitDistance = Vector3.Distance(hit.point, occlusionCamera.transform.position);
                
                if (hitDistance < objectDistance - 0.1f) // 添加小偏移避免自遮挡
                {
                    // 检查遮挡物的尺寸是否足够大
                    Renderer hitRenderer = hit.collider.GetComponent<Renderer>();
                    if (hitRenderer != null)
                    {
                        Bounds hitBounds = hitRenderer.bounds;
                        float hitScreenSize = CalculateScreenSpaceSize(hitBounds);
                        
                        if (hitScreenSize > node.screenSize * 0.5f)
                        {
                            isOccluded = true;
                            break;
                        }
                    }
                }
            }
            
            node.isVisible = !isOccluded;
            if (node.isVisible)
            {
                node.lastVisibleTime = Time.time;
            }
        }
        
        /// <summary>
        /// 计算屏幕空间大小
        /// </summary>
        private float CalculateScreenSpaceSize(Bounds bounds)
        {
            Vector3[] corners = GetBoundsCorners(bounds);
            float maxScreenSize = 0.0f;
            
            foreach (var corner in corners)
            {
                Vector3 screenPoint = occlusionCamera.WorldToViewportPoint(corner);
                
                if (screenPoint.z > 0)
                {
                    float screenSize = Mathf.Max(Mathf.Abs(screenPoint.x), Mathf.Abs(screenPoint.y));
                    maxScreenSize = Mathf.Max(maxScreenSize, screenSize);
                }
            }
            
            return maxScreenSize;
        }
        
        /// <summary>
        /// 获取包围盒的八个角
        /// </summary>
        private Vector3[] GetBoundsCorners(Bounds bounds)
        {
            Vector3[] corners = new Vector3[8];
            Vector3 center = bounds.center;
            Vector3 extents = bounds.extents;
            
            corners[0] = center + new Vector3(-extents.x, -extents.y, -extents.z);
            corners[1] = center + new Vector3(-extents.x, -extents.y, extents.z);
            corners[2] = center + new Vector3(-extents.x, extents.y, -extents.z);
            corners[3] = center + new Vector3(-extents.x, extents.y, extents.z);
            corners[4] = center + new Vector3(extents.x, -extents.y, -extents.z);
            corners[5] = center + new Vector3(extents.x, -extents.y, extents.z);
            corners[6] = center + new Vector3(extents.x, extents.y, -extents.z);
            corners[7] = center + new Vector3(extents.x, extents.y, extents.z);
            
            return corners;
        }
        
        /// <summary>
        /// 检查是否在视锥体内
        /// </summary>
        private bool IsInViewFrustum(Bounds bounds)
        {
            Plane[] planes = GeometryUtility.CalculateFrustumPlanes(occlusionCamera);
            return GeometryUtility.TestPlanesAABB(planes, bounds);
        }
        
        /// <summary>
        /// 检查精确遮挡
        /// </summary>
        private bool CheckPreciseOcclusion(Bounds bounds)
        {
            // 从相机向包围盒中心发射射线
            Vector3 direction = (bounds.center - occlusionCamera.transform.position).normalized;
            
            RaycastHit hit;
            if (Physics.Raycast(occlusionCamera.transform.position, direction, out hit, 
                Vector3.Distance(occlusionCamera.transform.position, bounds.center) * 1.1f, occluderLayer))
            {
                // 检查击中点是否在包围盒内
                if (bounds.Contains(hit.point))
                {
                    return false; // 击中自己,不是遮挡
                }
                return true; // 被其他物体遮挡
            }
            
            return false; // 没有遮挡
        }
        
        /// <summary>
        /// 完成待处理的查询
        /// </summary>
        private void CompletePendingQueries()
        {
            foreach (int queryId in pendingQueries)
            {
                availableQueryIds.Enqueue(queryId);
            }
            pendingQueries.Clear();
        }
        
        /// <summary>
        /// 更新可见性状态
        /// </summary>
        private void UpdateVisibilityStates()
        {
            foreach (var kvp in occlusionNodes)
            {
                OcclusionNode node = kvp.Value;
                Renderer renderer = kvp.Key;
                
                // 应用可见性状态
                if (renderer != null)
                {
                    renderer.enabled = node.isVisible;
                }
            }
        }
        
        /// <summary>
        /// 添加动态遮挡物
        /// </summary>
        public void RegisterDynamicOccluder(Renderer dynamicRenderer)
        {
            if (!occlusionNodes.ContainsKey(dynamicRenderer))
            {
                var node = new OcclusionNode
                {
                    renderer = dynamicRenderer,
                    worldBounds = dynamicRenderer.bounds,
                    lastVisibleTime = Time.time,
                    isVisible = true,
                    screenSize = 0.0f,
                    queryId = -1
                };
                
                occlusionNodes[dynamicRenderer] = node;
            }
        }
        
        /// <summary>
        /// 移除遮挡物
        /// </summary>
        public void UnregisterOccluder(Renderer renderer)
        {
            if (occlusionNodes.ContainsKey(renderer))
            {
                occlusionNodes.Remove(renderer);
            }
        }
        
        /// <summary>
        /// 手动强制更新对象的可见性
        /// </summary>
        public void ForceUpdateVisibility(Renderer renderer)
        {
            if (occlusionNodes.TryGetValue(renderer, out OcclusionNode node))
            {
                node.lastVisibleTime = Time.time - 1.0f; // 强制重新测试
            }
        }
        
        /// <summary>
        /// 获取系统统计信息
        /// </summary>
        public OcclusionStats GetStatistics()
        {
            int visibleCount = occlusionNodes.Values.Count(node => node.isVisible);
            int totalCount = occlusionNodes.Count;
            
            return new OcclusionStats
            {
                totalObjects = totalCount,
                visibleObjects = visibleCount,
                culledObjects = totalCount - visibleCount,
                cullingRatio = (float)(totalCount - visibleCount) / totalCount
            };
        }
        
        /// <summary>
        /// 遮挡系统统计信息
        /// </summary>
        public struct OcclusionStats
        {
            public int totalObjects;
            public int visibleObjects;
            public int culledObjects;
            public float cullingRatio;
        }
    }
}

10.2.2 动态遮挡剔除的事件驱动架构

在商业游戏中,遮挡剔除不仅需要高效,还需要灵活的事件系统来支持游戏逻辑。以下实现了一个事件驱动的遮挡系统:

using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;

namespace CommercialGame.OcclusionSystem
{
    /// <summary>
    /// 遮挡事件类型
    /// </summary>
    public enum OcclusionEventType
    {
        BecameVisible,
        BecameInvisible,
        PartialOcclusion,
        FullyVisible
    }
    
    /// <summary>
    /// 遮挡事件数据
    /// </summary>
    public class OcclusionEventData
    {
        public Renderer targetRenderer;
        public OcclusionEventType eventType;
        public float visibilityRatio;
        public Vector3 lastVisiblePosition;
        public float timestamp;
        
        public OcclusionEventData(Renderer renderer, OcclusionEventType type, float ratio = 1.0f)
        {
            targetRenderer = renderer;
            eventType = type;
            visibilityRatio = ratio;
            lastVisiblePosition = renderer.transform.position;
            timestamp = Time.time;
        }
    }
    
    /// <summary>
    /// 事件驱动的遮挡管理器
    /// </summary>
    public class EventDrivenOcclusionManager : MonoBehaviour
    {
        [System.Serializable]
        public class OcclusionEvent : UnityEvent<OcclusionEventData> { }
        
        [Header("事件配置")]
        public OcclusionEvent onBecameVisible = new OcclusionEvent();
        public OcclusionEvent onBecameInvisible = new OcclusionEvent();
        public OcclusionEvent onPartialOcclusion = new OcclusionEvent();
        public OcclusionEvent onFullyVisible = new OcclusionEvent();
        
        [Header("阈值配置")]
        [SerializeField]
        [Range(0.0f, 1.0f)]
        private float fullVisibilityThreshold = 0.9f;
        
        [SerializeField]
        [Range(0.0f, 1.0f)]
        private float partialOcclusionThreshold = 0.3f;
        
        [SerializeField]
        private float eventCooldown = 0.5f;
        
        private AdvancedOcclusionCuller occlusionCuller;
        private Dictionary<Renderer, OcclusionState> objectStates;
        private Dictionary<Renderer, float> lastEventTimes;
        
        private class OcclusionState
        {
            public bool wasVisible;
            public float lastVisibilityRatio;
            public int consecutiveFramesVisible;
            public int consecutiveFramesInvisible;
            public Vector3 lastRecordedPosition;
        }
        
        /// <summary>
        /// 初始化事件系统
        /// </summary>
        private void Start()
        {
            occlusionCuller = GetComponent<AdvancedOcclusionCuller>();
            if (occlusionCuller == null)
            {
                Debug.LogError("需要AdvancedOcclusionCuller组件");
                return;
            }
            
            objectStates = new Dictionary<Renderer, OcclusionState>();
            lastEventTimes = new Dictionary<Renderer, float>();
            
            // 初始收集所有渲染器
            InitializeObjectStates();
            
            Debug.Log("事件驱动遮挡系统初始化完成");
        }
        
        /// <summary>
        /// 初始化对象状态
        /// </summary>
        private void InitializeObjectStates()
        {
            Renderer[] allRenderers = FindObjectsOfType<Renderer>();
            
            foreach (Renderer renderer in allRenderers)
            {
                if (renderer.gameObject.isStatic || renderer.CompareTag("Dynamic"))
                {
                    objectStates[renderer] = new OcclusionState
                    {
                        wasVisible = renderer.enabled,
                        lastVisibilityRatio = renderer.enabled ? 1.0f : 0.0f,
                        consecutiveFramesVisible = renderer.enabled ? 1 : 0,
                        consecutiveFramesInvisible = renderer.enabled ? 0 : 1,
                        lastRecordedPosition = renderer.transform.position
                    };
                }
            }
        }
        
        /// <summary>
        /// 每帧更新事件检测
        /// </summary>
        private void Update()
        {
            if (occlusionCuller == null)
            {
                return;
            }
            
            // 获取当前帧的统计信息
            var stats = occlusionCuller.GetStatistics();
            
            // 检查所有跟踪的对象
            CheckOcclusionEvents();
        }
        
        /// <summary>
        /// 检查遮挡事件
        /// </summary>
        private void CheckOcclusionEvents()
        {
            foreach (var kvp in objectStates)
            {
                Renderer renderer = kvp.Key;
                OcclusionState state = kvp.Value;
                
                if (renderer == null)
                {
                    continue;
                }
                
                // 计算当前可见性比率
                float currentVisibilityRatio = CalculateVisibilityRatio(renderer);
                bool isCurrentlyVisible = currentVisibilityRatio > partialOcclusionThreshold;
                
                // 检测可见性变化
                if (isCurrentlyVisible != state.wasVisible)
                {
                    HandleVisibilityChange(renderer, isCurrentlyVisible, currentVisibilityRatio);
                }
                // 检测可见性程度变化
                else if (isCurrentlyVisible)
                {
                    HandleVisibilityRatioChange(renderer, currentVisibilityRatio, state);
                }
                
                // 更新状态
                UpdateOcclusionState(renderer, state, isCurrentlyVisible, currentVisibilityRatio);
            }
        }
        
        /// <summary>
        /// 计算可见性比率
        /// </summary>
        private float CalculateVisibilityRatio(Renderer renderer)
        {
            if (!renderer.enabled)
            {
                return 0.0f;
            }
            
            // 在实际项目中,这里应该实现更精确的可见性计算
            // 例如通过采样多个点或使用渲染查询
            
            Bounds bounds = renderer.bounds;
            Camera mainCamera = Camera.main;
            
            if (mainCamera == null)
            {
                return renderer.isVisible ? 1.0f : 0.0f;
            }
            
            // 简单实现:检查包围盒在屏幕上的投影面积
            Vector3[] corners = GetBoundsCorners(bounds);
            int visibleCorners = 0;
            
            foreach (Vector3 corner in corners)
            {
                Vector3 viewportPoint = mainCamera.WorldToViewportPoint(corner);
                
                // 检查点是否在视锥体内且未被遮挡
                if (viewportPoint.z > 0 && 
                    viewportPoint.x >= 0 && viewportPoint.x <= 1 &&
                    viewportPoint.y >= 0 && viewportPoint.y <= 1)
                {
                    visibleCorners++;
                }
            }
            
            return visibleCorners / (float)corners.Length;
        }
        
        /// <summary>
        /// 获取包围盒角点
        /// </summary>
        private Vector3[] GetBoundsCorners(Bounds bounds)
        {
            Vector3[] corners = new Vector3[8];
            Vector3 center = bounds.center;
            Vector3 extents = bounds.extents;
            
            corners[0] = center + new Vector3(-extents.x, -extents.y, -extents.z);
            corners[1] = center + new Vector3(-extents.x, -extents.y, extents.z);
            corners[2] = center + new Vector3(-extents.x, extents.y, -extents.z);
            corners[3] = center + new Vector3(-extents.x, extents.y, extents.z);
            corners[4] = center + new Vector3(extents.x, -extents.y, -extents.z);
            corners[5] = center + new Vector3(extents.x, -extents.y, extents.z);
            corners[6] = center + new Vector3(extents.x, extents.y, -extents.z);
            corners[7] = center + new Vector3(extents.x, extents.y, extents.z);
            
            return corners;
        }
        
        /// <summary>
        /// 处理可见性变化
        /// </summary>
        private void HandleVisibilityChange(Renderer renderer, bool becameVisible, float visibilityRatio)
        {
            // 检查事件冷却
            if (IsEventOnCooldown(renderer))
            {
                return;
            }
            
            OcclusionEventType eventType = becameVisible ? 
                OcclusionEventType.BecameVisible : 
                OcclusionEventType.BecameInvisible;
            
            var eventData = new OcclusionEventData(renderer, eventType, visibilityRatio);
            
            // 触发相应事件
            if (becameVisible)
            {
                onBecameVisible.Invoke(eventData);
            }
            else
            {
                onBecameInvisible.Invoke(eventData);
            }
            
            // 记录事件时间
            RecordEventTime(renderer);
            
            Debug.Log($"遮挡事件: {renderer.name} {(becameVisible ? "变为可见" : "变为不可见")}");
        }
        
        /// <summary>
        /// 处理可见性比率变化
        /// </summary>
        private void HandleVisibilityRatioChange(Renderer renderer, float currentRatio, OcclusionState state)
        {
            // 检查是否达到完全可见阈值
            if (currentRatio >= fullVisibilityThreshold && 
                state.lastVisibilityRatio < fullVisibilityThreshold)
            {
                TriggerFullyVisibleEvent(renderer, currentRatio);
            }
            // 检查是否进入部分遮挡状态
            else if (currentRatio < fullVisibilityThreshold && 
                     currentRatio > partialOcclusionThreshold &&
                     state.lastVisibilityRatio >= fullVisibilityThreshold)
            {
                TriggerPartialOcclusionEvent(renderer, currentRatio);
            }
        }
        
        /// <summary>
        /// 触发完全可见事件
        /// </summary>
        private void TriggerFullyVisibleEvent(Renderer renderer, float visibilityRatio)
        {
            if (IsEventOnCooldown(renderer))
            {
                return;
            }
            
            var eventData = new OcclusionEventData(renderer, OcclusionEventType.FullyVisible, visibilityRatio);
            onFullyVisible.Invoke(eventData);
            RecordEventTime(renderer);
        }
        
        /// <summary>
        /// 触发部分遮挡事件
        /// </summary>
        private void TriggerPartialOcclusionEvent(Renderer renderer, float visibilityRatio)
        {
            if (IsEventOnCooldown(renderer))
            {
                return;
            }
            
            var eventData = new OcclusionEventData(renderer, OcclusionEventType.PartialOcclusion, visibilityRatio);
            onPartialOcclusion.Invoke(eventData);
            RecordEventTime(renderer);
        }
        
        /// <summary>
        /// 更新遮挡状态
        /// </summary>
        private void UpdateOcclusionState(Renderer renderer, OcclusionState state, bool isVisible, float visibilityRatio)
        {
            state.wasVisible = isVisible;
            state.lastVisibilityRatio = visibilityRatio;
            
            if (isVisible)
            {
                state.consecutiveFramesVisible++;
                state.consecutiveFramesInvisible = 0;
            }
            else
            {
                state.consecutiveFramesInvisible++;
                state.consecutiveFramesVisible = 0;
            }
            
            // 记录位置变化
            if (Vector3.Distance(renderer.transform.position, state.lastRecordedPosition) > 0.1f)
            {
                state.lastRecordedPosition = renderer.transform.position;
            }
        }
        
        /// <summary>
        /// 检查事件是否在冷却中
        /// </summary>
        private bool IsEventOnCooldown(Renderer renderer)
        {
            if (lastEventTimes.TryGetValue(renderer, out float lastTime))
            {
                return Time.time - lastTime < eventCooldown;
            }
            return false;
        }
        
        /// <summary>
        /// 记录事件时间
        /// </summary>
        private void RecordEventTime(Renderer renderer)
        {
            lastEventTimes[renderer] = Time.time;
        }
        
        /// <summary>
        /// 注册动态对象
        /// </summary>
        public void RegisterDynamicObject(Renderer dynamicRenderer)
        {
            if (!objectStates.ContainsKey(dynamicRenderer))
            {
                objectStates[dynamicRenderer] = new OcclusionState
                {
                    wasVisible = dynamicRenderer.enabled,
                    lastVisibilityRatio = dynamicRenderer.enabled ? 1.0f : 0.0f,
                    consecutiveFramesVisible = dynamicRenderer.enabled ? 1 : 0,
                    consecutiveFramesInvisible = dynamicRenderer.enabled ? 0 : 1,
                    lastRecordedPosition = dynamicRenderer.transform.position
                };
                
                // 也注册到遮挡剔除器
                if (occlusionCuller != null)
                {
                    occlusionCuller.RegisterDynamicOccluder(dynamicRenderer);
                }
            }
        }
        
        /// <summary>
        /// 注销对象
        /// </summary>
        public void UnregisterObject(Renderer renderer)
        {
            objectStates.Remove(renderer);
            lastEventTimes.Remove(renderer);
        }
        
        /// <summary>
        /// 获取对象可见性信息
        /// </summary>
        public VisibilityInfo GetVisibilityInfo(Renderer renderer)
        {
            if (objectStates.TryGetValue(renderer, out OcclusionState state))
            {
                return new VisibilityInfo
                {
                    isVisible = state.wasVisible,
                    visibilityRatio = state.lastVisibilityRatio,
                    consecutiveVisibleFrames = state.consecutiveFramesVisible,
                    consecutiveInvisibleFrames = state.consecutiveFramesInvisible,
                    lastKnownPosition = state.lastRecordedPosition
                };
            }
            
            return new VisibilityInfo { isVisible = renderer.enabled };
        }
        
        /// <summary>
        /// 可见性信息结构
        /// </summary>
        public struct VisibilityInfo
        {
            public bool isVisible;
            public float visibilityRatio;
            public int consecutiveVisibleFrames;
            public int consecutiveInvisibleFrames;
            public Vector3 lastKnownPosition;
        }
    }
}

10.3 静态批处理与动态批处理优化技术

10.3.1 静态批处理的高级配置策略

静态批处理是Unity中减少绘制调用的关键技术,通过合并多个静态网格来减少CPU到GPU的通信开销。在商业项目中,需要平衡批处理效益和内存消耗。

以下是一个高级静态批处理管理器的实现:

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

namespace CommercialGame.BatchingSystem
{
    /// <summary>
    /// 高级静态批处理管理器
    /// </summary>
    public class AdvancedStaticBatching : MonoBehaviour
    {
        [System.Serializable]
        public class BatchSettings
        {
            public string batchName;
            public List<GameObject> batchObjects;
            public Material sharedMaterial;
            public int vertexLimit = 64000;
            public bool enableColliders = true;
            public bool preserveIndividualTransforms = false;
        }
        
        [Header("批处理配置")]
        [SerializeField]
        private List<BatchSettings> batchSettings = new List<BatchSettings>();
        
        [SerializeField]
        private bool autoCollectStaticObjects = true;
        
        [SerializeField]
        private string staticObjectTag = "Static";
        
        [SerializeField]
        private int maxBatchesPerFrame = 5;
        
        [Header("优化设置")]
        [SerializeField]
        private bool enableMeshCompression = true;
        
        [SerializeField]
        private bool removeDuplicateVertices = true;
        
        [SerializeField]
        private float weldVertexDistance = 0.001f;
        
        private Dictionary<string, GameObject> batchRoots;
        private Dictionary<Material, List<MeshFilter>> materialGroups;
        private Queue<BatchSettings> batchQueue;
        
        /// <summary>
        /// 初始化批处理系统
        /// </summary>
        private void Start()
        {
            batchRoots = new Dictionary<string, GameObject>();
            materialGroups = new Dictionary<Material, List<MeshFilter>>();
            batchQueue = new Queue<BatchSettings>();
            
            if (autoCollectStaticObjects)
            {
                CollectStaticObjects();
            }
            
            ProcessBatchQueue();
            
            Debug.Log($"静态批处理系统初始化完成,{batchSettings.Count} 个批次待处理");
        }
        
        /// <summary>
        /// 收集静态对象
        /// </summary>
        private void CollectStaticObjects()
        {
            GameObject[] staticObjects = GameObject.FindGameObjectsWithTag(staticObjectTag);
            
            // 按材质分组
            Dictionary<Material, List<GameObject>> materialObjectGroups = new Dictionary<Material, List<GameObject>>();
            
            foreach (GameObject obj in staticObjects)
            {
                if (!obj.isStatic)
                {
                    continue;
                }
                
                Renderer renderer = obj.GetComponent<Renderer>();
                if (renderer == null || renderer.sharedMaterial == null)
                {
                    continue;
                }
                
                MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
                if (meshFilter == null || meshFilter.sharedMesh == null)
                {
                    continue;
                }
                
                Material material = renderer.sharedMaterial;
                
                if (!materialObjectGroups.ContainsKey(material))
                {
                    materialObjectGroups[material] = new List<GameObject>();
                }
                
                materialObjectGroups[material].Add(obj);
            }
            
            // 创建批次设置
            int batchIndex = 0;
            foreach (var kvp in materialObjectGroups)
            {
                var batch = new BatchSettings
                {
                    batchName = $"StaticBatch_{batchIndex++}",
                    batchObjects = kvp.Value,
                    sharedMaterial = kvp.Key
                };
                
                batchSettings.Add(batch);
                batchQueue.Enqueue(batch);
            }
        }
        
        /// <summary>
        /// 处理批处理队列
        /// </summary>
        private void ProcessBatchQueue()
        {
            StartCoroutine(ProcessBatchesOverTime());
        }
        
        /// <summary>
        /// 分帧处理批次
        /// </summary>
        private System.Collections.IEnumerator ProcessBatchesOverTime()
        {
            int processedThisFrame = 0;
            
            while (batchQueue.Count > 0)
            {
                BatchSettings settings = batchQueue.Dequeue();
                ProcessSingleBatch(settings);
                
                processedThisFrame++;
                
                if (processedThisFrame >= maxBatchesPerFrame)
                {
                    processedThisFrame = 0;
                    yield return null;
                }
            }
            
            Debug.Log("所有批次处理完成");
        }
        
        /// <summary>
        /// 处理单个批次
        /// </summary>
        private void ProcessSingleBatch(BatchSettings settings)
        {
            if (settings.batchObjects == null || settings.batchObjects.Count == 0)
            {
                Debug.LogWarning($"批次 '{settings.batchName}' 没有对象");
                return;
            }
            
            // 创建批处理根对象
            GameObject batchRoot = new GameObject(settings.batchName);
            batchRoot.isStatic = true;
            batchRoot.transform.SetParent(transform);
            
            // 收集网格和变换信息
            List<CombineInstance> combineInstances = new List<CombineInstance>();
            List<Transform> originalTransforms = new List<Transform>();
            
            foreach (GameObject obj in settings.batchObjects)
            {
                if (obj == null)
                {
                    continue;
                }
                
                MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
                if (meshFilter == null || meshFilter.sharedMesh == null)
                {
                    continue;
                }
                
                // 创建组合实例
                CombineInstance combineInstance = new CombineInstance
                {
                    mesh = meshFilter.sharedMesh,
                    transform = meshFilter.transform.localToWorldMatrix,
                    subMeshIndex = 0
                };
                
                combineInstances.Add(combineInstance);
                originalTransforms.Add(obj.transform);
                
                // 禁用原始渲染器(可选)
                Renderer objRenderer = obj.GetComponent<Renderer>();
                if (objRenderer != null)
                {
                    objRenderer.enabled = false;
                }
            }
            
            if (combineInstances.Count == 0)
            {
                Destroy(batchRoot);
                return;
            }
            
            // 检查顶点数限制
            int totalVertices = combineInstances.Sum(ci => ci.mesh.vertexCount);
            if (totalVertices > settings.vertexLimit)
            {
                // 需要分割批次
                List<List<CombineInstance>> splitBatches = SplitBatchByVertexLimit(combineInstances, settings.vertexLimit);
                CreateMultipleBatches(splitBatches, settings, batchRoot, originalTransforms);
            }
            else
            {
                // 创建单个合并网格
                CreateCombinedMesh(combineInstances, settings, batchRoot, originalTransforms);
            }
            
            batchRoots[settings.batchName] = batchRoot;
            Debug.Log($"批次 '{settings.batchName}' 处理完成,合并了 {combineInstances.Count} 个网格");
        }
        
        /// <summary>
        /// 按顶点限制分割批次
        /// </summary>
        private List<List<CombineInstance>> SplitBatchByVertexLimit(List<CombineInstance> combineInstances, int vertexLimit)
        {
            List<List<CombineInstance>> splitBatches = new List<List<CombineInstance>>();
            List<CombineInstance> currentBatch = new List<CombineInstance>();
            int currentVertexCount = 0;
            
            foreach (CombineInstance instance in combineInstances)
            {
                int meshVertices = instance.mesh.vertexCount;
                
                if (currentVertexCount + meshVertices > vertexLimit && currentBatch.Count > 0)
                {
                    splitBatches.Add(new List<CombineInstance>(currentBatch));
                    currentBatch.Clear();
                    currentVertexCount = 0;
                }
                
                currentBatch.Add(instance);
                currentVertexCount += meshVertices;
            }
            
            if (currentBatch.Count > 0)
            {
                splitBatches.Add(currentBatch);
            }
            
            return splitBatches;
        }
        
        /// <summary>
        /// 创建多个批次
        /// </summary>
        private void CreateMultipleBatches(List<List<CombineInstance>> splitBatches, BatchSettings settings, 
                                         GameObject rootObject, List<Transform> originalTransforms)
        {
            for (int i = 0; i < splitBatches.Count; i++)
            {
                GameObject subBatchObject = new GameObject($"{settings.batchName}_Part{i}");
                subBatchObject.transform.SetParent(rootObject.transform);
                subBatchObject.isStatic = true;
                
                CreateCombinedMesh(splitBatches[i], settings, subBatchObject, originalTransforms);
            }
        }
        
        /// <summary>
        /// 创建合并的网格
        /// </summary>
        private void CreateCombinedMesh(List<CombineInstance> combineInstances, BatchSettings settings,
                                       GameObject batchObject, List<Transform> originalTransforms)
        {
            // 创建新网格
            Mesh combinedMesh = new Mesh();
            combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            
            // 合并网格
            combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
            
            // 网格优化
            OptimizeMesh(combinedMesh);
            
            // 添加网格组件
            MeshFilter meshFilter = batchObject.AddComponent<MeshFilter>();
            meshFilter.sharedMesh = combinedMesh;
            
            MeshRenderer meshRenderer = batchObject.AddComponent<MeshRenderer>();
            meshRenderer.sharedMaterial = settings.sharedMaterial;
            
            // 添加碰撞体(可选)
            if (settings.enableColliders)
            {
                AddOptimizedCollider(batchObject, combinedMesh);
            }
            
            // 保存原始变换信息(如果需要)
            if (settings.preserveIndividualTransforms)
            {
                SaveOriginalTransforms(batchObject, originalTransforms);
            }
        }
        
        /// <summary>
        /// 优化网格
        /// </summary>
        private void OptimizeMesh(Mesh mesh)
        {
            if (removeDuplicateVertices)
            {
                RemoveDuplicateVertices(mesh, weldVertexDistance);
            }
            
            if (enableMeshCompression)
            {
                mesh.Optimize();
                mesh.OptimizeIndexBuffers();
                mesh.OptimizeReorderVertexBuffer();
            }
            
            mesh.UploadMeshData(false); // 不标记为可读写以节省内存
        }
        
        /// <summary>
        /// 移除重复顶点
        /// </summary>
        private void RemoveDuplicateVertices(Mesh mesh, float weldDistance)
        {
            // 简化的重复顶点移除实现
            // 在实际项目中,可能需要更复杂的实现
            
            Vector3[] vertices = mesh.vertices;
            Dictionary<Vector3, List<int>> vertexMap = new Dictionary<Vector3, List<int>>();
            
            // 这里应该实现顶点焊接算法
            // 为简化示例,直接优化网格
            mesh.Optimize();
        }
        
        /// <summary>
        /// 添加优化碰撞体
        /// </summary>
        private void AddOptimizedCollider(GameObject gameObject, Mesh mesh)
        {
            // 根据网格大小选择合适的碰撞体类型
            Bounds meshBounds = mesh.bounds;
            float volume = meshBounds.size.x * meshBounds.size.y * meshBounds.size.z;
            
            if (volume < 10.0f) // 小对象使用网格碰撞体
            {
                MeshCollider meshCollider = gameObject.AddComponent<MeshCollider>();
                meshCollider.sharedMesh = mesh;
                meshCollider.convex = false;
            }
            else // 大对象使用简化碰撞体
            {
                BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>();
                boxCollider.center = meshBounds.center;
                boxCollider.size = meshBounds.size;
            }
        }
        
        /// <summary>
        /// 保存原始变换信息
        /// </summary>
        private void SaveOriginalTransforms(GameObject batchObject, List<Transform> originalTransforms)
        {
            var transformData = batchObject.AddComponent<BatchTransformData>();
            transformData.originalTransforms = originalTransforms.ToArray();
        }
        
        /// <summary>
        /// 手动添加批次
        /// </summary>
        public void AddBatch(BatchSettings settings)
        {
            batchSettings.Add(settings);
            batchQueue.Enqueue(settings);
        }
        
        /// <summary>
        /// 移除批次
        /// </summary>
        public void RemoveBatch(string batchName)
        {
            if (batchRoots.TryGetValue(batchName, out GameObject batchRoot))
            {
                Destroy(batchRoot);
                batchRoots.Remove(batchName);
                
                // 重新启用原始对象
                var batch = batchSettings.Find(b => b.batchName == batchName);
                if (batch != null)
                {
                    foreach (GameObject obj in batch.batchObjects)
                    {
                        if (obj != null)
                        {
                            Renderer renderer = obj.GetComponent<Renderer>();
                            if (renderer != null)
                            {
                                renderer.enabled = true;
                            }
                        }
                    }
                    
                    batchSettings.Remove(batch);
                }
            }
        }
        
        /// <summary>
        /// 获取批处理统计信息
        /// </summary>
        public BatchStatistics GetStatistics()
        {
            int totalBatches = batchRoots.Count;
            int totalOriginalObjects = batchSettings.Sum(b => b.batchObjects.Count);
            int totalCombinedObjects = batchRoots.Values
                .Sum(root => root.GetComponentsInChildren<MeshFilter>().Length);
            
            return new BatchStatistics
            {
                totalBatches = totalBatches,
                totalOriginalObjects = totalOriginalObjects,
                totalCombinedObjects = totalCombinedObjects,
                reductionRatio = (float)totalCombinedObjects / totalOriginalObjects
            };
        }
        
        /// <summary>
        /// 批处理统计信息
        /// </summary>
        public struct BatchStatistics
        {
            public int totalBatches;
            public int totalOriginalObjects;
            public int totalCombinedObjects;
            public float reductionRatio;
        }
    }
    
    /// <summary>
    /// 批处理变换数据组件
    /// </summary>
    public class BatchTransformData : MonoBehaviour
    {
        public Transform[] originalTransforms;
    }
}

10.3.2 动态批处理的智能管理系统

动态批处理针对移动对象,需要更智能的管理策略。以下实现了一个智能动态批处理系统:

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

namespace CommercialGame.BatchingSystem
{
    /// <summary>
    /// 智能动态批处理管理器
    /// </summary>
    public class IntelligentDynamicBatching : MonoBehaviour
    {
        [System.Serializable]
        public class DynamicBatchCriteria
        {
            public string criteriaName;
            public Material targetMaterial;
            public int maxBatchSize = 300;
            public float maxDistanceBetweenObjects = 50.0f;
            public float minScreenSize = 0.01f;
            public bool allowDifferentMeshes = true;
            public bool considerMovementSpeed = true;
            public float maxMovementSpeed = 10.0f;
        }
        
        [Header("批处理标准")]
        [SerializeField]
        private List<DynamicBatchCriteria> batchCriterias = new List<DynamicBatchCriteria>();
        
        [Header("性能设置")]
        [SerializeField]
        private int maxBatchesPerFrame = 3;
        
        [SerializeField]
        private float rebatchInterval = 2.0f;
        
        [SerializeField]
        private bool enableDistanceBasedGrouping = true;
        
        [SerializeField]
        private float groupingCellSize = 20.0f;
        
        [Header("调试设置")]
        [SerializeField]
        private bool visualizeBatches = false;
        
        [SerializeField]
        private Color[] batchColors;
        
        private class DynamicBatch
        {
            public string batchId;
            public List<GameObject> batchedObjects;
            public GameObject batchContainer;
            public Material batchMaterial;
            public float lastUpdateTime;
            public bool needsRebatch;
            public Color debugColor;
        }
        
        private class TrackedObject
        {
            public GameObject gameObject;
            public Renderer renderer;
            public MeshFilter meshFilter;
            public Material material;
            public Vector3 lastPosition;
            public float lastMovementTime;
            public string currentBatchId;
            public bool isEligibleForBatching;
        }
        
        private Dictionary<string, DynamicBatch> activeBatches;
        private Dictionary<GameObject, TrackedObject> trackedObjects;
        private Queue<GameObject> objectProcessingQueue;
        private float lastRebatchTime;
        private int frameCount;
        
        /// <summary>
        /// 初始化动态批处理系统
        /// </summary>
        private void Start()
        {
            InitializeBatchingSystem();
            
            // 初始收集可批处理对象
            CollectEligibleObjects();
            
            Debug.Log("智能动态批处理系统初始化完成");
        }
        
        /// <summary>
        /// 初始化批处理系统
        /// </summary>
        private void InitializeBatchingSystem()
        {
            activeBatches = new Dictionary<string, DynamicBatch>();
            trackedObjects = new Dictionary<GameObject, TrackedObject>();
            objectProcessingQueue = new Queue<GameObject>();
            lastRebatchTime = Time.time;
            
            // 初始化调试颜色
            if (batchColors == null || batchColors.Length == 0)
            {
                batchColors = new Color[]
                {
                    Color.red, Color.green, Color.blue, Color.yellow,
                    Color.cyan, Color.magenta, Color.white
                };
            }
        }
        
        /// <summary>
        /// 收集符合条件的对象
        /// </summary>
        private void CollectEligibleObjects()
        {
            // 查找所有动态对象
            GameObject[] allObjects = FindObjectsOfType<GameObject>();
            
            foreach (GameObject obj in allObjects)
            {
                if (obj.isStatic)
                {
                    continue; // 跳过静态对象
                }
                
                Renderer renderer = obj.GetComponent<Renderer>();
                MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
                
                if (renderer == null || meshFilter == null)
                {
                    continue;
                }
                
                if (renderer.sharedMaterial == null)
                {
                    continue;
                }
                
                // 检查是否符合批处理条件
                DynamicBatchCriteria criteria = FindMatchingCriteria(renderer.sharedMaterial);
                if (criteria == null)
                {
                    continue;
                }
                
                // 注册跟踪对象
                RegisterTrackedObject(obj, renderer, meshFilter, criteria);
            }
        }
        
        /// <summary>
        /// 查找匹配的批处理条件
        /// </summary>
        private DynamicBatchCriteria FindMatchingCriteria(Material material)
        {
            foreach (DynamicBatchCriteria criteria in batchCriterias)
            {
                if (criteria.targetMaterial == material)
                {
                    return criteria;
                }
            }
            return null;
        }
        
        /// <summary>
        /// 注册跟踪对象
        /// </summary>
        private void RegisterTrackedObject(GameObject obj, Renderer renderer, 
                                         MeshFilter meshFilter, DynamicBatchCriteria criteria)
        {
            var trackedObj = new TrackedObject
            {
                gameObject = obj,
                renderer = renderer,
                meshFilter = meshFilter,
                material = renderer.sharedMaterial,
                lastPosition = obj.transform.position,
                lastMovementTime = Time.time,
                currentBatchId = null,
                isEligibleForBatching = true
            };
            
            trackedObjects[obj] = trackedObj;
            objectProcessingQueue.Enqueue(obj);
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            frameCount++;
            
            // 处理对象队列
            ProcessObjectQueue();
            
            // 定期重新批处理
            if (Time.time - lastRebatchTime > rebatchInterval)
            {
                RevalidateAllBatches();
                lastRebatchTime = Time.time;
            }
            
            // 更新批处理状态
            UpdateBatchStates();
            
            // 调试可视化
            if (visualizeBatches)
            {
                VisualizeBatches();
            }
        }
        
        /// <summary>
        /// 处理对象队列
        /// </summary>
        private void ProcessObjectQueue()
        {
            int processedThisFrame = 0;
            
            while (objectProcessingQueue.Count > 0 && processedThisFrame < maxBatchesPerFrame)
            {
                GameObject obj = objectProcessingQueue.Dequeue();
                
                if (obj == null || !trackedObjects.ContainsKey(obj))
                {
                    continue;
                }
                
                TrackedObject trackedObj = trackedObjects[obj];
                
                if (!trackedObj.isEligibleForBatching)
                {
                    continue;
                }
                
                // 尝试将对象添加到现有批次或创建新批次
                AssignObjectToBatch(trackedObj);
                
                processedThisFrame++;
            }
        }
        
        /// <summary>
        /// 将对象分配到批次
        /// </summary>
        private void AssignObjectToBatch(TrackedObject trackedObj)
        {
            DynamicBatchCriteria criteria = FindMatchingCriteria(trackedObj.material);
            if (criteria == null)
            {
                trackedObj.isEligibleForBatching = false;
                return;
            }
            
            // 查找合适的现有批次
            DynamicBatch bestBatch = FindBestBatchForObject(trackedObj, criteria);
            
            if (bestBatch != null)
            {
                // 添加到现有批次
                AddObjectToBatch(trackedObj, bestBatch);
            }
            else
            {
                // 创建新批次
                CreateNewBatch(trackedObj, criteria);
            }
        }
        
        /// <summary>
        /// 查找最适合对象的批次
        /// </summary>
        private DynamicBatch FindBestBatchForObject(TrackedObject trackedObj, DynamicBatchCriteria criteria)
        {
            DynamicBatch bestBatch = null;
            float bestScore = float.MinValue;
            
            foreach (var kvp in activeBatches)
            {
                DynamicBatch batch = kvp.Value;
                
                if (batch.batchMaterial != trackedObj.material)
                {
                    continue;
                }
                
                if (batch.batchedObjects.Count >= criteria.maxBatchSize)
                {
                    continue;
                }
                
                // 计算批次适合度分数
                float score = CalculateBatchFitnessScore(trackedObj, batch, criteria);
                
                if (score > bestScore)
                {
                    bestScore = score;
                    bestBatch = batch;
                }
            }
            
            return bestBatch;
        }
        
        /// <summary>
        /// 计算批次适合度分数
        /// </summary>
        private float CalculateBatchFitnessScore(TrackedObject trackedObj, DynamicBatch batch, 
                                               DynamicBatchCriteria criteria)
        {
            float score = 0.0f;
            
            // 基于距离的评分
            if (enableDistanceBasedGrouping && batch.batchedObjects.Count > 0)
            {
                Vector3 avgPosition = CalculateAverageBatchPosition(batch);
                float distance = Vector3.Distance(trackedObj.gameObject.transform.position, avgPosition);
                
                if (distance <= criteria.maxDistanceBetweenObjects)
                {
                    score += (criteria.maxDistanceBetweenObjects - distance) / criteria.maxDistanceBetweenObjects;
                }
                else
                {
                    return float.MinValue; // 距离太远,不适合此批次
                }
            }
            
            // 基于屏幕大小的评分
            float screenSize = CalculateScreenSpaceSize(trackedObj.gameObject);
            if (screenSize >= criteria.minScreenSize)
            {
                score += screenSize * 10.0f;
            }
            
            // 基于运动状态的评分
            if (criteria.considerMovementSpeed)
            {
                float movementSpeed = CalculateMovementSpeed(trackedObj);
                if (movementSpeed <= criteria.maxMovementSpeed)
                {
                    score += (criteria.maxMovementSpeed - movementSpeed) / criteria.maxMovementSpeed;
                }
            }
            
            return score;
        }
        
        /// <summary>
        /// 计算批次的平均位置
        /// </summary>
        private Vector3 CalculateAverageBatchPosition(DynamicBatch batch)
        {
            Vector3 sum = Vector3.zero;
            
            foreach (GameObject obj in batch.batchedObjects)
            {
                sum += obj.transform.position;
            }
            
            return sum / batch.batchedObjects.Count;
        }
        
        /// <summary>
        /// 计算屏幕空间大小
        /// </summary>
        private float CalculateScreenSpaceSize(GameObject obj)
        {
            Camera mainCamera = Camera.main;
            if (mainCamera == null)
            {
                return 1.0f;
            }
            
            Renderer renderer = obj.GetComponent<Renderer>();
            if (renderer == null)
            {
                return 0.0f;
            }
            
            Bounds bounds = renderer.bounds;
            Vector3 screenSize = mainCamera.WorldToScreenPoint(bounds.max) - 
                               mainCamera.WorldToScreenPoint(bounds.min);
            
            return Mathf.Max(Mathf.Abs(screenSize.x), Mathf.Abs(screenSize.y)) / Screen.height;
        }
        
        /// <summary>
        /// 计算运动速度
        /// </summary>
        private float CalculateMovementSpeed(TrackedObject trackedObj)
        {
            float deltaTime = Time.time - trackedObj.lastMovementTime;
            if (deltaTime < 0.001f)
            {
                return 0.0f;
            }
            
            float distance = Vector3.Distance(trackedObj.gameObject.transform.position, trackedObj.lastPosition);
            return distance / deltaTime;
        }
        
        /// <summary>
        /// 将对象添加到批次
        /// </summary>
        private void AddObjectToBatch(TrackedObject trackedObj, DynamicBatch batch)
        {
            // 禁用原始渲染器
            trackedObj.renderer.enabled = false;
            
            // 添加到批次列表
            batch.batchedObjects.Add(trackedObj.gameObject);
            batch.lastUpdateTime = Time.time;
            
            // 更新批次网格
            UpdateBatchMesh(batch);
            
            trackedObj.currentBatchId = batch.batchId;
        }
        
        /// <summary>
        /// 创建新批次
        /// </summary>
        private void CreateNewBatch(TrackedObject trackedObj, DynamicBatchCriteria criteria)
        {
            string batchId = $"DynamicBatch_{activeBatches.Count}_{System.Guid.NewGuid().ToString().Substring(0, 8)}";
            
            // 创建批次容器
            GameObject batchContainer = new GameObject(batchId);
            batchContainer.transform.SetParent(transform);
            
            // 创建批次对象
            var batch = new DynamicBatch
            {
                batchId = batchId,
                batchedObjects = new List<GameObject> { trackedObj.gameObject },
                batchContainer = batchContainer,
                batchMaterial = trackedObj.material,
                lastUpdateTime = Time.time,
                needsRebatch = false,
                debugColor = batchColors[activeBatches.Count % batchColors.Length]
            };
            
            activeBatches[batchId] = batch;
            
            // 禁用原始渲染器
            trackedObj.renderer.enabled = false;
            trackedObj.currentBatchId = batchId;
            
            // 创建批次网格
            UpdateBatchMesh(batch);
            
            Debug.Log($"创建新动态批次: {batchId}");
        }
        
        /// <summary>
        /// 更新批次网格
        /// </summary>
        private void UpdateBatchMesh(DynamicBatch batch)
        {
            // 收集组合实例
            List<CombineInstance> combineInstances = new List<CombineInstance>();
            
            foreach (GameObject obj in batch.batchedObjects)
            {
                if (obj == null)
                {
                    continue;
                }
                
                MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
                if (meshFilter == null || meshFilter.sharedMesh == null)
                {
                    continue;
                }
                
                CombineInstance combineInstance = new CombineInstance
                {
                    mesh = meshFilter.sharedMesh,
                    transform = meshFilter.transform.localToWorldMatrix,
                    subMeshIndex = 0
                };
                
                combineInstances.Add(combineInstance);
            }
            
            if (combineInstances.Count == 0)
            {
                return;
            }
            
            // 清理旧的网格组件
            MeshFilter oldFilter = batch.batchContainer.GetComponent<MeshFilter>();
            MeshRenderer oldRenderer = batch.batchContainer.GetComponent<MeshRenderer>();
            
            if (oldFilter != null) Destroy(oldFilter);
            if (oldRenderer != null) Destroy(oldRenderer);
            
            // 创建新网格
            Mesh combinedMesh = new Mesh();
            combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
            
            // 添加新组件
            MeshFilter newFilter = batch.batchContainer.AddComponent<MeshFilter>();
            newFilter.sharedMesh = combinedMesh;
            
            MeshRenderer newRenderer = batch.batchContainer.AddComponent<MeshRenderer>();
            newRenderer.sharedMaterial = batch.batchMaterial;
            
            // 优化网格
            combinedMesh.Optimize();
            combinedMesh.UploadMeshData(false);
        }
        
        /// <summary>
        /// 重新验证所有批次
        /// </summary>
        private void RevalidateAllBatches()
        {
            foreach (var kvp in activeBatches)
            {
                DynamicBatch batch = kvp.Value;
                RevalidateBatch(batch);
            }
        }
        
        /// <summary>
        /// 重新验证批次
        /// </summary>
        private void RevalidateBatch(DynamicBatch batch)
        {
            // 检查批次中的每个对象是否仍然符合条件
            List<GameObject> objectsToRemove = new List<GameObject>();
            
            foreach (GameObject obj in batch.batchedObjects)
            {
                if (obj == null)
                {
                    objectsToRemove.Add(obj);
                    continue;
                }
                
                if (!trackedObjects.ContainsKey(obj))
                {
                    objectsToRemove.Add(obj);
                    continue;
                }
                
                TrackedObject trackedObj = trackedObjects[obj];
                
                // 检查对象是否仍然符合批处理条件
                if (!IsObjectStillEligible(trackedObj))
                {
                    objectsToRemove.Add(obj);
                }
            }
            
            // 移除不符合条件的对象
            foreach (GameObject obj in objectsToRemove)
            {
                RemoveObjectFromBatch(obj, batch);
            }
            
            // 如果批次为空,销毁它
            if (batch.batchedObjects.Count == 0)
            {
                Destroy(batch.batchContainer);
                activeBatches.Remove(batch.batchId);
            }
            else if (objectsToRemove.Count > 0)
            {
                // 更新批次网格
                UpdateBatchMesh(batch);
            }
        }
        
        /// <summary>
        /// 检查对象是否仍然符合条件
        /// </summary>
        private bool IsObjectStillEligible(TrackedObject trackedObj)
        {
            if (trackedObj.gameObject == null)
            {
                return false;
            }
            
            // 检查对象是否被销毁
            if (!trackedObj.renderer || !trackedObj.meshFilter)
            {
                return false;
            }
            
            // 检查材质是否改变
            DynamicBatchCriteria criteria = FindMatchingCriteria(trackedObj.renderer.sharedMaterial);
            if (criteria == null)
            {
                return false;
            }
            
            // 检查距离条件
            if (enableDistanceBasedGrouping)
            {
                // 这里应该实现更精确的距离检查
            }
            
            // 检查运动速度
            if (criteria.considerMovementSpeed)
            {
                float movementSpeed = CalculateMovementSpeed(trackedObj);
                if (movementSpeed > criteria.maxMovementSpeed)
                {
                    return false;
                }
            }
            
            return true;
        }
        
        /// <summary>
        /// 从批次中移除对象
        /// </summary>
        private void RemoveObjectFromBatch(GameObject obj, DynamicBatch batch)
        {
            batch.batchedObjects.Remove(obj);
            
            // 重新启用原始渲染器
            if (trackedObjects.TryGetValue(obj, out TrackedObject trackedObj))
            {
                if (trackedObj.renderer != null)
                {
                    trackedObj.renderer.enabled = true;
                }
                trackedObj.currentBatchId = null;
            }
        }
        
        /// <summary>
        /// 更新批次状态
        /// </summary>
        private void UpdateBatchStates()
        {
            // 更新所有跟踪对象的位置信息
            foreach (var kvp in trackedObjects)
            {
                TrackedObject trackedObj = kvp.Value;
                
                if (trackedObj.gameObject != null)
                {
                    trackedObj.lastPosition = trackedObj.gameObject.transform.position;
                    trackedObj.lastMovementTime = Time.time;
                }
            }
        }
        
        /// <summary>
        /// 可视化批次
        /// </summary>
        private void VisualizeBatches()
        {
            foreach (var kvp in activeBatches)
            {
                DynamicBatch batch = kvp.Value;
                
                if (batch.batchContainer != null)
                {
                    // 绘制批次包围盒
                    Bounds batchBounds = CalculateBatchBounds(batch);
                    DebugDrawBounds(batchBounds, batch.debugColor);
                }
            }
        }
        
        /// <summary>
        /// 计算批次包围盒
        /// </summary>
        private Bounds CalculateBatchBounds(DynamicBatch batch)
        {
            Bounds bounds = new Bounds();
            bool first = true;
            
            foreach (GameObject obj in batch.batchedObjects)
            {
                if (obj == null)
                {
                    continue;
                }
                
                Renderer renderer = obj.GetComponent<Renderer>();
                if (renderer == null)
                {
                    continue;
                }
                
                if (first)
                {
                    bounds = renderer.bounds;
                    first = false;
                }
                else
                {
                    bounds.Encapsulate(renderer.bounds);
                }
            }
            
            return bounds;
        }
        
        /// <summary>
        /// 调试绘制包围盒
        /// </summary>
        private void DebugDrawBounds(Bounds bounds, Color color)
        {
            Vector3 center = bounds.center;
            Vector3 size = bounds.size;
            
            Vector3[] corners = new Vector3[8];
            corners[0] = center + new Vector3(-size.x, -size.y, -size.z) * 0.5f;
            corners[1] = center + new Vector3(-size.x, -size.y, size.z) * 0.5f;
            corners[2] = center + new Vector3(-size.x, size.y, -size.z) * 0.5f;
            corners[3] = center + new Vector3(-size.x, size.y, size.z) * 0.5f;
            corners[4] = center + new Vector3(size.x, -size.y, -size.z) * 0.5f;
            corners[5] = center + new Vector3(size.x, -size.y, size.z) * 0.5f;
            corners[6] = center + new Vector3(size.x, size.y, -size.z) * 0.5f;
            corners[7] = center + new Vector3(size.x, size.y, size.z) * 0.5f;
            
            // 绘制包围盒边
            Debug.DrawLine(corners[0], corners[1], color);
            Debug.DrawLine(corners[0], corners[2], color);
            Debug.DrawLine(corners[0], corners[4], color);
            Debug.DrawLine(corners[1], corners[3], color);
            Debug.DrawLine(corners[1], corners[5], color);
            Debug.DrawLine(corners[2], corners[3], color);
            Debug.DrawLine(corners[2], corners[6], color);
            Debug.DrawLine(corners[3], corners[7], color);
            Debug.DrawLine(corners[4], corners[5], color);
            Debug.DrawLine(corners[4], corners[6], color);
            Debug.DrawLine(corners[5], corners[7], color);
            Debug.DrawLine(corners[6], corners[7], color);
        }
        
        /// <summary>
        /// 注册新对象进行动态批处理
        /// </summary>
        public void RegisterDynamicObject(GameObject obj)
        {
            if (obj == null || obj.isStatic)
            {
                return;
            }
            
            Renderer renderer = obj.GetComponent<Renderer>();
            MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
            
            if (renderer == null || meshFilter == null)
            {
                return;
            }
            
            DynamicBatchCriteria criteria = FindMatchingCriteria(renderer.sharedMaterial);
            if (criteria == null)
            {
                return;
            }
            
            if (!trackedObjects.ContainsKey(obj))
            {
                RegisterTrackedObject(obj, renderer, meshFilter, criteria);
            }
        }
        
        /// <summary>
        /// 取消注册对象
        /// </summary>
        public void UnregisterObject(GameObject obj)
        {
            if (trackedObjects.TryGetValue(obj, out TrackedObject trackedObj))
            {
                // 如果对象在批次中,先移除它
                if (!string.IsNullOrEmpty(trackedObj.currentBatchId))
                {
                    if (activeBatches.TryGetValue(trackedObj.currentBatchId, out DynamicBatch batch))
                    {
                        RemoveObjectFromBatch(obj, batch);
                    }
                }
                
                trackedObjects.Remove(obj);
            }
        }
        
        /// <summary>
        /// 获取系统统计信息
        /// </summary>
        public DynamicBatchStatistics GetStatistics()
        {
            int totalBatches = activeBatches.Count;
            int totalBatchedObjects = activeBatches.Values.Sum(batch => batch.batchedObjects.Count);
            int totalTrackedObjects = trackedObjects.Count;
            
            return new DynamicBatchStatistics
            {
                totalBatches = totalBatches,
                totalBatchedObjects = totalBatchedObjects,
                totalTrackedObjects = totalTrackedObjects,
                batchingRatio = totalTrackedObjects > 0 ? (float)totalBatchedObjects / totalTrackedObjects : 0.0f
            };
        }
        
        /// <summary>
        /// 动态批处理统计信息
        /// </summary>
        public struct DynamicBatchStatistics
        {
            public int totalBatches;
            public int totalBatchedObjects;
            public int totalTrackedObjects;
            public float batchingRatio;
        }
    }
}

10.4 导航网格系统的实现与优化

10.4.1 寻路系统的数学基础与配置

寻路系统的核心是A*(A-Star)算法,其评估函数为:f(n) = g(n) + h(n),其中g(n)是从起点到当前节点的实际代价,h(n)是启发式函数估计的当前节点到目标的代价。

在Unity的NavMesh系统中,还涉及以下数学概念:

  1. 网格生成:使用体素化和区域生长算法
  2. 路径平滑:使用贝塞尔曲线或Catmull-Rom样条
  3. 动态障碍:使用局部障碍图和势力场

以下是一个高级寻路系统的实现:

using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
using System.Linq;

namespace CommercialGame.NavigationSystem
{
    /// <summary>
    /// 高级导航网格管理器
    /// </summary>
    public class AdvancedNavMeshManager : MonoBehaviour
    {
        [System.Serializable]
        public class NavMeshAreaConfig
        {
            public string areaName;
            public int areaCost = 1;
            public Color debugColor = Color.white;
            public bool walkable = true;
        }
        
        [Header("导航网格配置")]
        [SerializeField]
        private List<NavMeshAreaConfig> areaConfigs = new List<NavMeshAreaConfig>();
        
        [SerializeField]
        private LayerMask navMeshBuildLayers = -1;
        
        [SerializeField]
        private float agentRadius = 0.5f;
        
        [SerializeField]
        private float agentHeight = 2.0f;
        
        [SerializeField]
        private float maxSlope = 45.0f;
        
        [SerializeField]
        private float stepHeight = 0.3f;
        
        [Header("性能设置")]
        [SerializeField]
        private int maxPathFinders = 10;
        
        [SerializeField]
        private float pathUpdateInterval = 0.5f;
        
        [SerializeField]
        private bool useMultithreadedPathfinding = true;
        
        [Header("动态障碍设置")]
        [SerializeField]
        private bool enableDynamicObstacles = true;
        
        [SerializeField]
        private float obstacleUpdateRate = 2.0f;
        
        [SerializeField]
        private float obstacleCarveRadius = 1.0f;
        
        private Dictionary<string, NavMeshAreaConfig> areaConfigDictionary;
        private List<NavMeshPathFinder> activePathFinders;
        private Queue<PathRequest> pathRequestQueue;
        private Dictionary<int, NavMeshObstacle> dynamicObstacles;
        private NavMeshData navMeshData;
        private NavMeshDataInstance navMeshInstance;
        private float lastObstacleUpdateTime;
        
        private class PathRequest
        {
            public Vector3 start;
            public Vector3 end;
            public int agentTypeId;
            public System.Action<NavMeshPath, bool> callback;
            public float timestamp;
        }
        
        /// <summary>
        /// 初始化导航系统
        /// </summary>
        private void Awake()
        {
            InitializeNavigationSystem();
            BuildNavMesh();
            
            Debug.Log("高级导航网格系统初始化完成");
        }
        
        /// <summary>
        /// 初始化导航系统
        /// </summary>
        private void InitializeNavigationSystem()
        {
            areaConfigDictionary = new Dictionary<string, NavMeshAreaConfig>();
            activePathFinders = new List<NavMeshPathFinder>();
            pathRequestQueue = new Queue<PathRequest>();
            dynamicObstacles = new Dictionary<int, NavMeshObstacle>();
            
            // 初始化区域配置字典
            foreach (NavMeshAreaConfig config in areaConfigs)
            {
                areaConfigDictionary[config.areaName] = config;
            }
            
            // 创建路径查找器池
            CreatePathFinderPool();
        }
        
        /// <summary>
        /// 构建导航网格
        /// </summary>
        private void BuildNavMesh()
        {
            // 创建导航网格构建设置
            NavMeshBuildSettings buildSettings = NavMesh.GetSettingsByID(0);
            buildSettings.agentRadius = agentRadius;
            buildSettings.agentHeight = agentHeight;
            buildSettings.agentSlope = maxSlope;
            buildSettings.agentClimb = stepHeight;
            
            // 收集场景中的导航网格源
            List<NavMeshBuildSource> sources = new List<NavMeshBuildSource>();
            CollectNavMeshSources(sources);
            
            // 定义导航网格边界
            Bounds bounds = CalculateSceneBounds();
            
            // 异步构建导航网格
            NavMeshBuilder.UpdateNavMeshDataAsync(
                navMeshData,
                buildSettings,
                sources,
                bounds
            );
            
            // 注册导航网格数据
            if (navMeshInstance.valid)
            {
                navMeshInstance.Remove();
            }
            navMeshInstance = NavMesh.AddNavMeshData(navMeshData);
        }
        
        /// <summary>
        /// 收集导航网格源
        /// </summary>
        private void CollectNavMeshSources(List<NavMeshBuildSource> sources)
        {
            // 收集网格渲染器
            MeshRenderer[] meshRenderers = FindObjectsOfType<MeshRenderer>();
            
            foreach (MeshRenderer renderer in meshRenderers)
            {
                if (((1 << renderer.gameObject.layer) & navMeshBuildLayers) == 0)
                {
                    continue;
                }
                
                MeshFilter meshFilter = renderer.GetComponent<MeshFilter>();
                if (meshFilter == null || meshFilter.sharedMesh == null)
                {
                    continue;
                }
                
                NavMeshBuildSource source = new NavMeshBuildSource
                {
                    shape = NavMeshBuildSourceShape.Mesh,
                    sourceObject = meshFilter.sharedMesh,
                    transform = renderer.transform.localToWorldMatrix,
                    area = GetAreaFromGameObject(renderer.gameObject)
                };
                
                sources.Add(source);
            }
            
            // 收集地形
            Terrain[] terrains = FindObjectsOfType<Terrain>();
            foreach (Terrain terrain in terrains)
            {
                if (((1 << terrain.gameObject.layer) & navMeshBuildLayers) == 0)
                {
                    continue;
                }
                
                NavMeshBuildSource source = new NavMeshBuildSource
                {
                    shape = NavMeshBuildSourceShape.Terrain,
                    sourceObject = terrain.terrainData,
                    transform = terrain.transform.localToWorldMatrix,
                    area = GetAreaFromGameObject(terrain.gameObject)
                };
                
                sources.Add(source);
            }
        }
        
        /// <summary>
        /// 从游戏对象获取区域类型
        /// </summary>
        private int GetAreaFromGameObject(GameObject gameObject)
        {
            // 检查对象标签或组件以确定区域类型
            foreach (var kvp in areaConfigDictionary)
            {
                NavMeshAreaConfig config = kvp.Value;
                
                // 这里可以根据实际需求实现更复杂的逻辑
                if (gameObject.CompareTag(kvp.Key))
                {
                    return NavMesh.GetAreaFromName(kvp.Key);
                }
            }
            
            return 0; // 默认区域
        }
        
        /// <summary>
        /// 计算场景边界
        /// </summary>
        private Bounds CalculateSceneBounds()
        {
            // 收集所有需要包含在导航网格中的对象
            List<Renderer> includedRenderers = new List<Renderer>();
            Renderer[] allRenderers = FindObjectsOfType<Renderer>();
            
            foreach (Renderer renderer in allRenderers)
            {
                if (((1 << renderer.gameObject.layer) & navMeshBuildLayers) != 0)
                {
                    includedRenderers.Add(renderer);
                }
            }
            
            if (includedRenderers.Count == 0)
            {
                return new Bounds(Vector3.zero, Vector3.one * 100);
            }
            
            // 计算合并的边界
            Bounds bounds = includedRenderers[0].bounds;
            for (int i = 1; i < includedRenderers.Count; i++)
            {
                bounds.Encapsulate(includedRenderers[i].bounds);
            }
            
            // 扩大边界以确保完全覆盖
            bounds.Expand(10.0f);
            
            return bounds;
        }
        
        /// <summary>
        /// 创建路径查找器池
        /// </summary>
        private void CreatePathFinderPool()
        {
            for (int i = 0; i < maxPathFinders; i++)
            {
                var pathFinder = new NavMeshPathFinder();
                activePathFinders.Add(pathFinder);
            }
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            UpdatePathRequests();
            UpdateDynamicObstacles();
        }
        
        /// <summary>
        /// 更新路径请求
        /// </summary>
        private void UpdatePathRequests()
        {
            if (pathRequestQueue.Count == 0)
            {
                return;
            }
            
            // 查找空闲的路径查找器
            NavMeshPathFinder freeFinder = activePathFinders.Find(pf => !pf.IsProcessing);
            
            if (freeFinder != null && pathRequestQueue.Count > 0)
            {
                PathRequest request = pathRequestQueue.Dequeue();
                ProcessPathRequest(request, freeFinder);
            }
        }
        
        /// <summary>
        /// 处理路径请求
        /// </summary>
        private void ProcessPathRequest(PathRequest request, NavMeshPathFinder pathFinder)
        {
            pathFinder.IsProcessing = true;
            
            if (useMultithreadedPathfinding)
            {
                // 在后台线程中计算路径
                System.Threading.ThreadPool.QueueUserWorkItem(state =>
                {
                    NavMeshPath path = new NavMeshPath();
                    bool success = NavMesh.CalculatePath(request.start, request.end, NavMesh.AllAreas, path);
                    
                    // 回到主线程调用回调
                    this.RunOnMainThread(() =>
                    {
                        request.callback?.Invoke(path, success);
                        pathFinder.IsProcessing = false;
                    });
                });
            }
            else
            {
                // 在主线程中计算路径
                NavMeshPath path = new NavMeshPath();
                bool success = NavMesh.CalculatePath(request.start, request.end, NavMesh.AllAreas, path);
                
                request.callback?.Invoke(path, success);
                pathFinder.IsProcessing = false;
            }
        }
        
        /// <summary>
        /// 更新动态障碍
        /// </summary>
        private void UpdateDynamicObstacles()
        {
            if (!enableDynamicObstacles || Time.time - lastObstacleUpdateTime < obstacleUpdateRate)
            {
                return;
            }
            
            // 更新所有动态障碍的位置和大小
            foreach (var kvp in dynamicObstacles)
            {
                UpdateDynamicObstacle(kvp.Value);
            }
            
            lastObstacleUpdateTime = Time.time;
        }
        
        /// <summary>
        /// 更新动态障碍
        /// </summary>
        private void UpdateDynamicObstacle(NavMeshObstacle obstacle)
        {
            if (obstance == null)
            {
                return;
            }
            
            // 更新障碍的雕刻(需要Unity 2019.3+)
            obstacle.carving = true;
            obstacle.carveOnlyStationary = false;
        }
        
        /// <summary>
        /// 请求路径
        /// </summary>
        public void RequestPath(Vector3 start, Vector3 end, System.Action<NavMeshPath, bool> callback)
        {
            PathRequest request = new PathRequest
            {
                start = start,
                end = end,
                callback = callback,
                timestamp = Time.time
            };
            
            pathRequestQueue.Enqueue(request);
        }
        
        /// <summary>
        /// 添加动态障碍
        /// </summary>
        public int AddDynamicObstacle(Vector3 position, float radius, float height = 2.0f)
        {
            if (!enableDynamicObstacles)
            {
                return -1;
            }
            
            GameObject obstacleObj = new GameObject($"DynamicObstacle_{dynamicObstacles.Count}");
            obstacleObj.transform.position = position;
            
            NavMeshObstacle obstacle = obstacleObj.AddComponent<NavMeshObstacle>();
            obstacle.shape = NavMeshObstacleShape.Capsule;
            obstacle.radius = radius;
            obstacle.height = height;
            obstacle.carving = true;
            obstacle.carveOnlyStationary = false;
            
            int obstacleId = obstacle.GetInstanceID();
            dynamicObstacles[obstacleId] = obstacle;
            
            return obstacleId;
        }
        
        /// <summary>
        /// 移除动态障碍
        /// </summary>
        public void RemoveDynamicObstacle(int obstacleId)
        {
            if (dynamicObstacles.TryGetValue(obstacleId, out NavMeshObstacle obstacle))
            {
                if (obstacle != null)
                {
                    Destroy(obstacle.gameObject);
                }
                dynamicObstacles.Remove(obstacleId);
            }
        }
        
        /// <summary>
        /// 获取导航网格统计信息
        /// </summary>
        public NavMeshStatistics GetStatistics()
        {
            NavMeshTriangulation triangulation = NavMesh.CalculateTriangulation();
            
            return new NavMeshStatistics
            {
                totalVertices = triangulation.vertices.Length,
                totalTriangles = triangulation.indices.Length / 3,
                totalAreas = areaConfigs.Count,
                activePathFinders = activePathFinders.Count(pf => pf.IsProcessing),
                queuedRequests = pathRequestQueue.Count,
                dynamicObstacles = dynamicObstacles.Count
            };
        }
        
        /// <summary>
        /// 导航网格统计信息
        /// </summary>
        public struct NavMeshStatistics
        {
            public int totalVertices;
            public int totalTriangles;
            public int totalAreas;
            public int activePathFinders;
            public int queuedRequests;
            public int dynamicObstacles;
        }
        
        /// <summary>
        /// 路径查找器类
        /// </summary>
        private class NavMeshPathFinder
        {
            public bool IsProcessing { get; set; }
            public float LastUseTime { get; set; }
        }
    }
    
    /// <summary>
    /// Unity主线程运行助手
    /// </summary>
    public static class UnityThreadHelper
    {
        private static System.Action pendingActions;
        private static readonly object lockObject = new object();
        
        public static void RunOnMainThread(System.Action action)
        {
            lock (lockObject)
            {
                pendingActions += action;
            }
        }
        
        public static void ExecutePendingActions()
        {
            System.Action actionsToExecute = null;
            
            lock (lockObject)
            {
                if (pendingActions != null)
                {
                    actionsToExecute = pendingActions;
                    pendingActions = null;
                }
            }
            
            actionsToExecute?.Invoke();
        }
    }
}

10.4.2 寻路路径的优化与平滑处理

寻路得到的原始路径通常由直线段组成,需要通过平滑处理来获得更自然的移动路径。常用的平滑算法包括:

  1. 贝塞尔曲线平滑:使用二次或三次贝塞尔曲线
  2. Catmull-Rom样条:保证通过所有控制点
  3. 梯度下降优化:通过迭代优化路径点位置

以下是一个路径平滑系统的实现:

using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;

namespace CommercialGame.NavigationSystem
{
    /// <summary>
    /// 路径平滑优化器
    /// </summary>
    public class PathSmoother : MonoBehaviour
    {
        [System.Serializable]
        public class SmoothingSettings
        {
            public SmoothingAlgorithm algorithm = SmoothingAlgorithm.Bezier;
            [Range(0, 1)]
            public float smoothingFactor = 0.5f;
            public int iterations = 3;
            public float maxDeviation = 0.5f;
            public bool preserveCorners = true;
            public float cornerAngleThreshold = 45.0f;
        }
        
        public enum SmoothingAlgorithm
        {
            Linear,
            Bezier,
            CatmullRom,
            Chaikin
        }
        
        [Header("平滑设置")]
        [SerializeField]
        private SmoothingSettings defaultSettings = new SmoothingSettings();
        
        [Header("性能设置")]
        [SerializeField]
        private int maxPathPoints = 1024;
        
        [SerializeField]
        private bool useSimplification = true;
        
        [SerializeField]
        private float simplificationTolerance = 0.1f;
        
        private Dictionary<int, SmoothingSettings> agentSettings;
        
        /// <summary>
        /// 初始化路径平滑器
        /// </summary>
        private void Awake()
        {
            agentSettings = new Dictionary<int, SmoothingSettings>();
        }
        
        /// <summary>
        /// 平滑路径
        /// </summary>
        public Vector3[] SmoothPath(Vector3[] originalPath, int agentId = 0)
        {
            if (originalPath == null || originalPath.Length < 2)
            {
                return originalPath;
            }
            
            // 获取代理的平滑设置
            SmoothingSettings settings = GetAgentSettings(agentId);
            
            // 简化路径(可选)
            Vector3[] simplifiedPath = originalPath;
            if (useSimplification && originalPath.Length > 3)
            {
                simplifiedPath = SimplifyPath(originalPath, settings);
            }
            
            // 应用平滑算法
            Vector3[] smoothedPath = ApplySmoothingAlgorithm(simplifiedPath, settings);
            
            // 确保路径仍然有效
            smoothedPath = ValidateSmoothedPath(smoothedPath, originalPath, settings);
            
            return smoothedPath;
        }
        
        /// <summary>
        /// 简化路径
        /// </summary>
        private Vector3[] SimplifyPath(Vector3[] path, SmoothingSettings settings)
        {
            List<Vector3> simplified = new List<Vector3>();
            
            // 添加起点
            simplified.Add(path[0]);
            
            // Douglas-Peucker算法简化
            DouglasPeucker(path, 0, path.Length - 1, simplificationTolerance, ref simplified);
            
            // 添加终点
            if (!simplified.Contains(path[path.Length - 1]))
            {
                simplified.Add(path[path.Length - 1]);
            }
            
            return simplified.ToArray();
        }
        
        /// <summary>
        /// Douglas-Peucker路径简化算法
        /// </summary>
        private void DouglasPeucker(Vector3[] points, int startIndex, int endIndex, 
                                   float tolerance, ref List<Vector3> result)
        {
            if (endIndex <= startIndex + 1)
            {
                return;
            }
            
            float maxDistance = 0;
            int maxIndex = startIndex;
            
            Vector3 startPoint = points[startIndex];
            Vector3 endPoint = points[endIndex];
            
            // 找到离线段最远的点
            for (int i = startIndex + 1; i < endIndex; i++)
            {
                float distance = PointToLineDistance(points[i], startPoint, endPoint);
                
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                    maxIndex = i;
                }
            }
            
            // 如果最远点大于容差,递归处理
            if (maxDistance > tolerance)
            {
                DouglasPeucker(points, startIndex, maxIndex, tolerance, ref result);
                result.Add(points[maxIndex]);
                DouglasPeucker(points, maxIndex, endIndex, tolerance, ref result);
            }
        }
        
        /// <summary>
        /// 计算点到线段的距离
        /// </summary>
        private float PointToLineDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
        {
            Vector3 line = lineEnd - lineStart;
            float lineLength = line.magnitude;
            
            if (lineLength < 0.001f)
            {
                return Vector3.Distance(point, lineStart);
            }
            
            Vector3 normalizedLine = line / lineLength;
            Vector3 pointVector = point - lineStart;
            
            // 计算投影长度
            float projectionLength = Vector3.Dot(pointVector, normalizedLine);
            
            if (projectionLength < 0)
            {
                return Vector3.Distance(point, lineStart);
            }
            else if (projectionLength > lineLength)
            {
                return Vector3.Distance(point, lineEnd);
            }
            else
            {
                Vector3 projectionPoint = lineStart + normalizedLine * projectionLength;
                return Vector3.Distance(point, projectionPoint);
            }
        }
        
        /// <summary>
        /// 应用平滑算法
        /// </summary>
        private Vector3[] ApplySmoothingAlgorithm(Vector3[] path, SmoothingSettings settings)
        {
            switch (settings.algorithm)
            {
                case SmoothingAlgorithm.Bezier:
                    return ApplyBezierSmoothing(path, settings);
                    
                case SmoothingAlgorithm.CatmullRom:
                    return ApplyCatmullRomSmoothing(path, settings);
                    
                case SmoothingAlgorithm.Chaikin:
                    return ApplyChaikinSmoothing(path, settings);
                    
                case SmoothingAlgorithm.Linear:
                default:
                    return path;
            }
        }
        
        /// <summary>
        /// 应用贝塞尔曲线平滑
        /// </summary>
        private Vector3[] ApplyBezierSmoothing(Vector3[] path, SmoothingSettings settings)
        {
            if (path.Length < 3)
            {
                return path;
            }
            
            List<Vector3> smoothedPath = new List<Vector3>();
            
            // 添加起点
            smoothedPath.Add(path[0]);
            
            // 为每三个点创建二次贝塞尔曲线
            for (int i = 0; i < path.Length - 2; i += 2)
            {
                Vector3 p0 = path[i];
                Vector3 p1 = path[i + 1];
                Vector3 p2 = path[i + 2];
                
                // 生成贝塞尔曲线点
                for (float t = 0.1f; t < 1.0f; t += 0.1f)
                {
                    Vector3 bezierPoint = CalculateQuadraticBezierPoint(t, p0, p1, p2);
                    smoothedPath.Add(bezierPoint);
                }
            }
            
            // 添加终点
            smoothedPath.Add(path[path.Length - 1]);
            
            return smoothedPath.ToArray();
        }
        
        /// <summary>
        /// 计算二次贝塞尔曲线点
        /// </summary>
        private Vector3 CalculateQuadraticBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
        {
            float u = 1 - t;
            float tt = t * t;
            float uu = u * u;
            
            Vector3 point = uu * p0; // (1-t)² * P0
            point += 2 * u * t * p1; // 2(1-t)t * P1
            point += tt * p2; // t² * P2
            
            return point;
        }
        
        /// <summary>
        /// 应用Catmull-Rom样条平滑
        /// </summary>
        private Vector3[] ApplyCatmullRomSmoothing(Vector3[] path, SmoothingSettings settings)
        {
            if (path.Length < 4)
            {
                return path;
            }
            
            List<Vector3> smoothedPath = new List<Vector3>();
            
            // 添加第一个点
            smoothedPath.Add(path[0]);
            
            // 为每四个点创建Catmull-Rom样条
            for (int i = 0; i < path.Length - 3; i++)
            {
                Vector3 p0 = i == 0 ? path[0] : path[i - 1];
                Vector3 p1 = path[i];
                Vector3 p2 = path[i + 1];
                Vector3 p3 = path[i + 2];
                
                // 生成样条点
                for (float t = 0; t <= 1.0f; t += 0.1f)
                {
                    Vector3 splinePoint = CalculateCatmullRomPoint(t, p0, p1, p2, p3);
                    smoothedPath.Add(splinePoint);
                }
            }
            
            // 添加最后两个点
            smoothedPath.Add(path[path.Length - 2]);
            smoothedPath.Add(path[path.Length - 1]);
            
            return smoothedPath.ToArray();
        }
        
        /// <summary>
        /// 计算Catmull-Rom样条点
        /// </summary>
        private Vector3 CalculateCatmullRomPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
        {
            Vector3 point = 0.5f * (
                (2 * p1) +
                (-p0 + p2) * t +
                (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
                (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
            );
            
            return point;
        }
        
        /// <summary>
        /// 应用Chaikin平滑
        /// </summary>
        private Vector3[] ApplyChaikinSmoothing(Vector3[] path, SmoothingSettings settings)
        {
            List<Vector3> currentPath = new List<Vector3>(path);
            
            // 应用多次Chaikin平滑迭代
            for (int iteration = 0; iteration < settings.iterations; iteration++)
            {
                List<Vector3> newPath = new List<Vector3>();
                
                // 添加第一个点
                newPath.Add(currentPath[0]);
                
                // 对每一对点应用Chaikin算法
                for (int i = 0; i < currentPath.Count - 1; i++)
                {
                    Vector3 p0 = currentPath[i];
                    Vector3 p1 = currentPath[i + 1];
                    
                    // 计算两个新点
                    Vector3 q = Vector3.Lerp(p0, p1, settings.smoothingFactor);
                    Vector3 r = Vector3.Lerp(p0, p1, 1 - settings.smoothingFactor);
                    
                    newPath.Add(q);
                    newPath.Add(r);
                }
                
                // 添加最后一个点
                newPath.Add(currentPath[currentPath.Count - 1]);
                
                currentPath = newPath;
            }
            
            return currentPath.ToArray();
        }
        
        /// <summary>
        /// 验证平滑后的路径
        /// </summary>
        private Vector3[] ValidateSmoothedPath(Vector3[] smoothedPath, Vector3[] originalPath, 
                                             SmoothingSettings settings)
        {
            List<Vector3> validatedPath = new List<Vector3>();
            
            // 检查每个点是否在导航网格上
            for (int i = 0; i < smoothedPath.Length; i++)
            {
                Vector3 point = smoothedPath[i];
                
                // 检查点是否可达
                if (IsPointReachable(point, settings.maxDeviation))
                {
                    validatedPath.Add(point);
                }
                else
                {
                    // 如果不可达,使用原始路径中最接近的点
                    Vector3 closestValidPoint = FindClosestValidPoint(point, originalPath);
                    validatedPath.Add(closestValidPoint);
                }
            }
            
            // 保留转角(可选)
            if (settings.preserveCorners)
            {
                validatedPath = PreserveSharpCorners(validatedPath, settings.cornerAngleThreshold);
            }
            
            return validatedPath.ToArray();
        }
        
        /// <summary>
        /// 检查点是否可达
        /// </summary>
        private bool IsPointReachable(Vector3 point, float maxDeviation)
        {
            NavMeshHit hit;
            return NavMesh.SamplePosition(point, out hit, maxDeviation, NavMesh.AllAreas);
        }
        
        /// <summary>
        /// 查找最近的有效点
        /// </summary>
        private Vector3 FindClosestValidPoint(Vector3 point, Vector3[] originalPath)
        {
            Vector3 closestPoint = point;
            float closestDistance = float.MaxValue;
            
            foreach (Vector3 originalPoint in originalPath)
            {
                float distance = Vector3.Distance(point, originalPoint);
                if (distance < closestDistance && IsPointReachable(originalPoint, 0.1f))
                {
                    closestDistance = distance;
                    closestPoint = originalPoint;
                }
            }
            
            return closestPoint;
        }
        
        /// <summary>
        /// 保留锐利转角
        /// </summary>
        private List<Vector3> PreserveSharpCorners(List<Vector3> path, float angleThreshold)
        {
            if (path.Count < 3)
            {
                return path;
            }
            
            List<Vector3> result = new List<Vector3>();
            
            // 添加第一个点
            result.Add(path[0]);
            
            // 检查每个点是否是转角
            for (int i = 1; i < path.Count - 1; i++)
            {
                Vector3 prev = path[i - 1];
                Vector3 current = path[i];
                Vector3 next = path[i + 1];
                
                // 计算转角角度
                Vector3 dir1 = (current - prev).normalized;
                Vector3 dir2 = (next - current).normalized;
                
                float angle = Vector3.Angle(dir1, dir2);
                
                // 如果是锐角,保留该点
                if (angle < angleThreshold)
                {
                    result.Add(current);
                }
            }
            
            // 添加最后一个点
            result.Add(path[path.Count - 1]);
            
            return result;
        }
        
        /// <summary>
        /// 获取代理设置
        /// </summary>
        private SmoothingSettings GetAgentSettings(int agentId)
        {
            if (agentSettings.TryGetValue(agentId, out SmoothingSettings settings))
            {
                return settings;
            }
            
            // 使用默认设置
            return defaultSettings;
        }
        
        /// <summary>
        /// 设置代理的平滑参数
        /// </summary>
        public void SetAgentSmoothingSettings(int agentId, SmoothingSettings settings)
        {
            agentSettings[agentId] = settings;
        }
        
        /// <summary>
        /// 清除代理设置
        /// </summary>
        public void ClearAgentSettings(int agentId)
        {
            agentSettings.Remove(agentId);
        }
        
        /// <summary>
        /// 可视化路径
        /// </summary>
        public void VisualizePath(Vector3[] path, Color color, float duration = 0)
        {
            if (path == null || path.Length < 2)
            {
                return;
            }
            
            for (int i = 0; i < path.Length - 1; i++)
            {
                Debug.DrawLine(path[i], path[i + 1], color, duration);
            }
        }
    }
}

10.5 反射探头的配置与混合策略

反射探头用于提供高质量的局部反射效果,特别是在室内场景和金属材质中。以下是一个高级反射探头管理系统:

using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;

namespace CommercialGame.ReflectionSystem
{
    /// <summary>
    /// 高级反射探头管理器
    /// </summary>
    public class AdvancedReflectionProbeManager : MonoBehaviour
    {
        [System.Serializable]
        public class ProbeSettings
        {
            public ReflectionProbe probe;
            public float importance = 1.0f;
            public float blendDistance = 5.0f;
            public bool dynamicUpdate = false;
            public float updateInterval = 1.0f;
            public ProbeVolumeShape volumeShape = ProbeVolumeShape.Box;
            public Vector3 volumeSize = Vector3.one * 10;
        }
        
        public enum ProbeVolumeShape
        {
            Box,
            Sphere
        }
        
        [Header("探头管理")]
        [SerializeField]
        private List<ProbeSettings> probeSettings = new List<ProbeSettings>();
        
        [SerializeField]
        private ReflectionProbe globalProbe;
        
        [SerializeField]
        private bool autoCollectProbes = true;
        
        [Header("混合设置")]
        [SerializeField]
        private bool enableProbeBlending = true;
        
        [SerializeField]
        private int maxBlendedProbes = 2;
        
        [SerializeField]
        private float blendSmoothness = 2.0f;
        
        [Header("性能设置")]
        [SerializeField]
        private bool useAsyncUpdates = true;
        
        [SerializeField]
        private int maxUpdatesPerFrame = 1;
        
        [SerializeField]
        private bool cullDistantProbes = true;
        
        [SerializeField]
        private float maxProbeDistance = 50.0f;
        
        private Dictionary<ReflectionProbe, ProbeSettings> probeSettingsDictionary;
        private Dictionary<ReflectionProbe, float> probeUpdateTimes;
        private Queue<ReflectionProbe> probeUpdateQueue;
        private int updatesThisFrame;
        
        /// <summary>
        /// 初始化反射探头系统
        /// </summary>
        private void Awake()
        {
            InitializeReflectionSystem();
            
            if (autoCollectProbes)
            {
                CollectSceneProbes();
            }
            
            Debug.Log($"反射探头系统初始化完成,管理 {probeSettings.Count} 个探头");
        }
        
        /// <summary>
        /// 初始化反射系统
        /// </summary>
        private void InitializeReflectionSystem()
        {
            probeSettingsDictionary = new Dictionary<ReflectionProbe, ProbeSettings>();
            probeUpdateTimes = new Dictionary<ReflectionProbe, float>();
            probeUpdateQueue = new Queue<ReflectionProbe>();
            
            // 注册所有探头
            foreach (ProbeSettings settings in probeSettings)
            {
                if (settings.probe != null)
                {
                    probeSettingsDictionary[settings.probe] = settings;
                    probeUpdateTimes[settings.probe] = Time.time;
                    
                    // 配置探头
                    ConfigureProbe(settings);
                }
            }
        }
        
        /// <summary>
        /// 收集场景中的探头
        /// </summary>
        private void CollectSceneProbes()
        {
            ReflectionProbe[] allProbes = FindObjectsOfType<ReflectionProbe>();
            
            foreach (ReflectionProbe probe in allProbes)
            {
                // 跳过已经注册的探头
                if (probeSettingsDictionary.ContainsKey(probe))
                {
                    continue;
                }
                
                // 创建新的探头设置
                ProbeSettings settings = new ProbeSettings
                {
                    probe = probe,
                    importance = probe.importance,
                    blendDistance = probe.blendDistance,
                    dynamicUpdate = probe.mode == ReflectionProbeMode.Realtime,
                    updateInterval = probe.refreshMode == ReflectionProbeRefreshMode.ViaScripting ? 1.0f : 0,
                    volumeShape = ProbeVolumeShape.Box,
                    volumeSize = probe.size
                };
                
                probeSettings.Add(settings);
                probeSettingsDictionary[probe] = settings;
                probeUpdateTimes[probe] = Time.time;
                
                // 配置探头
                ConfigureProbe(settings);
            }
        }
        
        /// <summary>
        /// 配置探头
        /// </summary>
        private void ConfigureProbe(ProbeSettings settings)
        {
            ReflectionProbe probe = settings.probe;
            
            // 设置混合距离
            probe.blendDistance = settings.blendDistance;
            
            // 配置动态更新
            if (settings.dynamicUpdate)
            {
                probe.mode = ReflectionProbeMode.Realtime;
                probe.refreshMode = ReflectionProbeRefreshMode.ViaScripting;
                probe.timeSlicingMode = ReflectionProbeTimeSlicingMode.AllFacesAtATime;
            }
            else
            {
                probe.mode = ReflectionProbeMode.Baked;
            }
            
            // 设置重要性
            probe.importance = Mathf.RoundToInt(settings.importance);
            
            Debug.Log($"配置反射探头: {probe.name}, 模式: {probe.mode}, 重要性: {probe.importance}");
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            updatesThisFrame = 0;
            
            // 更新动态探头
            UpdateDynamicProbes();
            
            // 处理探头更新队列
            ProcessProbeUpdateQueue();
        }
        
        /// <summary>
        /// 更新动态探头
        /// </summary>
        private void UpdateDynamicProbes()
        {
            foreach (var kvp in probeSettingsDictionary)
            {
                ReflectionProbe probe = kvp.Key;
                ProbeSettings settings = kvp.Value;
                
                if (!settings.dynamicUpdate)
                {
                    continue;
                }
                
                // 检查是否需要更新
                if (Time.time - probeUpdateTimes[probe] >= settings.updateInterval)
                {
                    // 检查探头是否在范围内
                    if (cullDistantProbes)
                    {
                        float distance = Vector3.Distance(Camera.main.transform.position, probe.transform.position);
                        if (distance > maxProbeDistance)
                        {
                            continue;
                        }
                    }
                    
                    // 添加到更新队列
                    probeUpdateQueue.Enqueue(probe);
                    probeUpdateTimes[probe] = Time.time;
                }
            }
        }
        
        /// <summary>
        /// 处理探头更新队列
        /// </summary>
        private void ProcessProbeUpdateQueue()
        {
            while (probeUpdateQueue.Count > 0 && updatesThisFrame < maxUpdatesPerFrame)
            {
                ReflectionProbe probe = probeUpdateQueue.Dequeue();
                
                if (probe != null)
                {
                    UpdateProbe(probe);
                    updatesThisFrame++;
                }
            }
        }
        
        /// <summary>
        /// 更新探头
        /// </summary>
        private void UpdateProbe(ReflectionProbe probe)
        {
            if (useAsyncUpdates)
            {
                // 异步更新
                probe.RenderProbe();
            }
            else
            {
                // 同步更新
                probe.RenderProbe();
            }
            
            Debug.Log($"更新反射探头: {probe.name}");
        }
        
        /// <summary>
        /// 为位置获取最佳反射探头混合
        /// </summary>
        public ReflectionProbeBlendInfo GetBestProbeBlend(Vector3 position, bool includeGlobal = true)
        {
            ReflectionProbeBlendInfo blendInfo = new ReflectionProbeBlendInfo();
            
            if (!enableProbeBlending)
            {
                // 不混合,返回最近的探头
                blendInfo.primaryProbe = GetNearestProbe(position);
                blendInfo.secondaryProbe = null;
                blendInfo.blendFactor = 1.0f;
                return blendInfo;
            }
            
            // 收集所有影响该位置的探头
            List<ProbeInfluence> influencingProbes = GetInfluencingProbes(position);
            
            if (influencingProbes.Count == 0 && includeGlobal && globalProbe != null)
            {
                // 没有局部探头,使用全局探头
                blendInfo.primaryProbe = globalProbe;
                blendInfo.secondaryProbe = null;
                blendInfo.blendFactor = 1.0f;
                return blendInfo;
            }
            
            // 按影响力排序
            influencingProbes.Sort((a, b) => b.influence.CompareTo(a.influence));
            
            // 选择最重要的探头
            if (influencingProbes.Count >= 1)
            {
                blendInfo.primaryProbe = influencingProbes[0].probe;
                
                if (influencingProbes.Count >= 2 && maxBlendedProbes >= 2)
                {
                    blendInfo.secondaryProbe = influencingProbes[1].probe;
                    
                    // 计算混合因子
                    float totalInfluence = influencingProbes[0].influence + influencingProbes[1].influence;
                    if (totalInfluence > 0)
                    {
                        blendInfo.blendFactor = influencingProbes[0].influence / totalInfluence;
                        
                        // 应用平滑
                        blendInfo.blendFactor = Mathf.SmoothStep(0, 1, blendInfo.blendFactor * blendSmoothness);
                    }
                    else
                    {
                        blendInfo.blendFactor = 0.5f;
                    }
                }
                else
                {
                    blendInfo.secondaryProbe = null;
                    blendInfo.blendFactor = 1.0f;
                }
            }
            
            return blendInfo;
        }
        
        /// <summary>
        /// 获取影响位置的探头
        /// </summary>
        private List<ProbeInfluence> GetInfluencingProbes(Vector3 position)
        {
            List<ProbeInfluence> influencingProbes = new List<ProbeInfluence>();
            
            foreach (var kvp in probeSettingsDictionary)
            {
                ReflectionProbe probe = kvp.Key;
                ProbeSettings settings = kvp.Value;
                
                // 检查位置是否在探头体积内
                if (IsPositionInProbeVolume(position, probe, settings))
                {
                    // 计算影响力
                    float influence = CalculateProbeInfluence(position, probe, settings);
                    
                    if (influence > 0.01f) // 忽略影响力太小的探头
                    {
                        influencingProbes.Add(new ProbeInfluence
                        {
                            probe = probe,
                            influence = influence
                        });
                    }
                }
            }
            
            return influencingProbes;
        }
        
        /// <summary>
        /// 检查位置是否在探头体积内
        /// </summary>
        private bool IsPositionInProbeVolume(Vector3 position, ReflectionProbe probe, ProbeSettings settings)
        {
            Vector3 localPosition = probe.transform.InverseTransformPoint(position);
            
            switch (settings.volumeShape)
            {
                case ProbeVolumeShape.Box:
                    return Mathf.Abs(localPosition.x) <= settings.volumeSize.x * 0.5f &&
                           Mathf.Abs(localPosition.y) <= settings.volumeSize.y * 0.5f &&
                           Mathf.Abs(localPosition.z) <= settings.volumeSize.z * 0.5f;
                    
                case ProbeVolumeShape.Sphere:
                    return localPosition.magnitude <= settings.volumeSize.x * 0.5f;
                    
                default:
                    return false;
            }
        }
        
        /// <summary>
        /// 计算探头影响力
        /// </summary>
        private float CalculateProbeInfluence(Vector3 position, ReflectionProbe probe, ProbeSettings settings)
        {
            // 计算到探头中心的距离
            float distance = Vector3.Distance(position, probe.transform.position);
            
            // 计算体积边界距离
            float volumeDistance = GetDistanceToVolumeBoundary(position, probe, settings);
            
            // 基于距离的影响力衰减
            float distanceInfluence = Mathf.Clamp01(1.0f - volumeDistance / settings.blendDistance);
            
            // 考虑探头重要性
            float importanceFactor = settings.importance / 10.0f; // 归一化到0-1
            
            // 组合影响力
            float influence = distanceInfluence * importanceFactor;
            
            return influence;
        }
        
        /// <summary>
        /// 获取到体积边界的距离
        /// </summary>
        private float GetDistanceToVolumeBoundary(Vector3 position, ReflectionProbe probe, ProbeSettings settings)
        {
            Vector3 localPosition = probe.transform.InverseTransformPoint(position);
            
            switch (settings.volumeShape)
            {
                case ProbeVolumeShape.Box:
                    // 计算到长方体表面的最近距离
                    Vector3 halfSize = settings.volumeSize * 0.5f;
                    Vector3 clampedPosition = new Vector3(
                        Mathf.Clamp(localPosition.x, -halfSize.x, halfSize.x),
                        Mathf.Clamp(localPosition.y, -halfSize.y, halfSize.y),
                        Mathf.Clamp(localPosition.z, -halfSize.z, halfSize.z)
                    );
                    
                    return Vector3.Distance(localPosition, clampedPosition);
                    
                case ProbeVolumeShape.Sphere:
                    // 计算到球体表面的距离
                    float radius = settings.volumeSize.x * 0.5f;
                    return Mathf.Max(0, localPosition.magnitude - radius);
                    
                default:
                    return float.MaxValue;
            }
        }
        
        /// <summary>
        /// 获取最近的探头
        /// </summary>
        private ReflectionProbe GetNearestProbe(Vector3 position)
        {
            ReflectionProbe nearestProbe = null;
            float nearestDistance = float.MaxValue;
            
            foreach (var kvp in probeSettingsDictionary)
            {
                ReflectionProbe probe = kvp.Key;
                float distance = Vector3.Distance(position, probe.transform.position);
                
                if (distance < nearestDistance)
                {
                    nearestDistance = distance;
                    nearestProbe = probe;
                }
            }
            
            return nearestProbe;
        }
        
        /// <summary>
        /// 手动强制更新探头
        /// </summary>
        public void ForceUpdateProbe(ReflectionProbe probe)
        {
            if (probe != null)
            {
                probeUpdateQueue.Enqueue(probe);
                probeUpdateTimes[probe] = Time.time;
            }
        }
        
        /// <summary>
        /// 添加新探头
        /// </summary>
        public void AddProbe(ReflectionProbe probe, ProbeSettings settings = null)
        {
            if (probe == null)
            {
                Debug.LogWarning("尝试添加空探头");
                return;
            }
            
            if (probeSettingsDictionary.ContainsKey(probe))
            {
                Debug.LogWarning($"探头 {probe.name} 已经存在");
                return;
            }
            
            if (settings == null)
            {
                settings = new ProbeSettings
                {
                    probe = probe,
                    importance = probe.importance,
                    blendDistance = probe.blendDistance,
                    dynamicUpdate = probe.mode == ReflectionProbeMode.Realtime
                };
            }
            
            probeSettings.Add(settings);
            probeSettingsDictionary[probe] = settings;
            probeUpdateTimes[probe] = Time.time;
            
            ConfigureProbe(settings);
            
            Debug.Log($"添加反射探头: {probe.name}");
        }
        
        /// <summary>
        /// 移除探头
        /// </summary>
        public void RemoveProbe(ReflectionProbe probe)
        {
            if (probeSettingsDictionary.ContainsKey(probe))
            {
                ProbeSettings settings = probeSettingsDictionary[probe];
                
                probeSettings.Remove(settings);
                probeSettingsDictionary.Remove(probe);
                probeUpdateTimes.Remove(probe);
                
                Debug.Log($"移除反射探头: {probe.name}");
            }
        }
        
        /// <summary>
        /// 获取系统统计信息
        /// </summary>
        public ReflectionProbeStatistics GetStatistics()
        {
            int totalProbes = probeSettingsDictionary.Count;
            int dynamicProbes = 0;
            int queuedUpdates = probeUpdateQueue.Count;
            
            foreach (var settings in probeSettings)
            {
                if (settings.dynamicUpdate)
                {
                    dynamicProbes++;
                }
            }
            
            return new ReflectionProbeStatistics
            {
                totalProbes = totalProbes,
                dynamicProbes = dynamicProbes,
                staticProbes = totalProbes - dynamicProbes,
                queuedUpdates = queuedUpdates,
                updatesThisFrame = updatesThisFrame
            };
        }
        
        /// <summary>
        /// 反射探头统计信息
        /// </summary>
        public struct ReflectionProbeStatistics
        {
            public int totalProbes;
            public int dynamicProbes;
            public int staticProbes;
            public int queuedUpdates;
            public int updatesThisFrame;
        }
        
        /// <summary>
        /// 探头影响力结构
        /// </summary>
        private struct ProbeInfluence
        {
            public ReflectionProbe probe;
            public float influence;
        }
        
        /// <summary>
        /// 反射探头混合信息
        /// </summary>
        public struct ReflectionProbeBlendInfo
        {
            public ReflectionProbe primaryProbe;
            public ReflectionProbe secondaryProbe;
            public float blendFactor;
        }
    }
}

10.6 综合优化策略与性能监控

在商业游戏开发中,各种优化技术需要协同工作,并且需要实时监控系统性能。以下是一个综合性能监控系统的实现:

using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommercialGame.PerformanceSystem
{
    /// <summary>
    /// 综合性能监控器
    /// </summary>
    public class ComprehensivePerformanceMonitor : MonoBehaviour
    {
        [System.Serializable]
        public class PerformanceThresholds
        {
            public float targetFPS = 60.0f;
            public float warningFPS = 30.0f;
            public float criticalFPS = 20.0f;
            public int targetDrawCalls = 500;
            public int warningDrawCalls = 1000;
            public int criticalDrawCalls = 2000;
            public float targetFrameTime = 16.67f; // 60 FPS对应的帧时间
            public float warningFrameTime = 33.33f; // 30 FPS对应的帧时间
            public float criticalFrameTime = 50.0f; // 20 FPS对应的帧时间
        }
        
        [Header("性能阈值")]
        [SerializeField]
        private PerformanceThresholds thresholds = new PerformanceThresholds();
        
        [Header("监控设置")]
        [SerializeField]
        private float updateInterval = 1.0f;
        
        [SerializeField]
        private int sampleCount = 60;
        
        [SerializeField]
        private bool logWarnings = true;
        
        [SerializeField]
        private bool autoOptimize = false;
        
        [Header("子系统监控")]
        [SerializeField]
        private bool monitorLighting = true;
        
        [SerializeField]
        private bool monitorOcclusion = true;
        
        [SerializeField]
        private bool monitorBatching = true;
        
        [SerializeField]
        private bool monitorNavigation = true;
        
        [SerializeField]
        private bool monitorReflections = true;
        
        private class PerformanceSample
        {
            public float timestamp;
            public float fps;
            public float frameTime;
            public int drawCalls;
            public int triangles;
            public int vertices;
            public float memoryUsage;
            public LightingStats lightingStats;
            public OcclusionStats occlusionStats;
            public BatchingStats batchingStats;
            public NavigationStats navigationStats;
            public ReflectionStats reflectionStats;
        }
        
        private Queue<PerformanceSample> performanceSamples;
        private PerformanceSample currentSample;
        private float lastUpdateTime;
        private int frameCount;
        private float accumulatedFrameTime;
        
        // 子系统引用
        private AdvancedStaticBatching batchingSystem;
        private AdvancedOcclusionCuller occlusionSystem;
        private AdvancedNavMeshManager navigationSystem;
        private AdvancedReflectionProbeManager reflectionSystem;
        private HybridLightingManager lightingSystem;
        
        /// <summary>
        /// 初始化性能监控器
        /// </summary>
        private void Awake()
        {
            InitializeMonitor();
            FindSubsystems();
        }
        
        /// <summary>
        /// 初始化监控器
        /// </summary>
        private void InitializeMonitor()
        {
            performanceSamples = new Queue<PerformanceSample>(sampleCount);
            currentSample = new PerformanceSample();
            lastUpdateTime = Time.time;
            frameCount = 0;
            accumulatedFrameTime = 0;
            
            Debug.Log("综合性能监控器初始化完成");
        }
        
        /// <summary>
        /// 查找子系统
        /// </summary>
        private void FindSubsystems()
        {
            batchingSystem = FindObjectOfType<AdvancedStaticBatching>();
            occlusionSystem = FindObjectOfType<AdvancedOcclusionCuller>();
            navigationSystem = FindObjectOfType<AdvancedNavMeshManager>();
            reflectionSystem = FindObjectOfType<AdvancedReflectionProbeManager>();
            lightingSystem = FindObjectOfType<HybridLightingManager>();
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            frameCount++;
            accumulatedFrameTime += Time.unscaledDeltaTime;
            
            // 定期收集性能数据
            if (Time.time - lastUpdateTime >= updateInterval)
            {
                CollectPerformanceData();
                AnalyzePerformance();
                
                if (autoOptimize)
                {
                    ApplyAutoOptimizations();
                }
                
                lastUpdateTime = Time.time;
            }
        }
        
        /// <summary>
        /// 收集性能数据
        /// </summary>
        private void CollectPerformanceData()
        {
            currentSample = new PerformanceSample
            {
                timestamp = Time.time,
                fps = frameCount / accumulatedFrameTime,
                frameTime = accumulatedFrameTime / frameCount * 1000.0f, // 转换为毫秒
                drawCalls = UnityEngine.Rendering.OnPreCull.GetInvocationList().Length, // 近似值
                triangles = 0, // 需要通过统计获取
                vertices = 0, // 需要通过统计获取
                memoryUsage = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong() / 1024.0f / 1024.0f // MB
            };
            
            // 收集子系统数据
            if (monitorLighting && lightingSystem != null)
            {
                // 这里应该从光照系统获取统计数据
                currentSample.lightingStats = new LightingStats();
            }
            
            if (monitorOcclusion && occlusionSystem != null)
            {
                var stats = occlusionSystem.GetStatistics();
                currentSample.occlusionStats = new OcclusionStats
                {
                    totalObjects = stats.totalObjects,
                    visibleObjects = stats.visibleObjects,
                    cullingRatio = stats.cullingRatio
                };
            }
            
            if (monitorBatching && batchingSystem != null)
            {
                var stats = batchingSystem.GetStatistics();
                currentSample.batchingStats = new BatchingStats
                {
                    totalBatches = stats.totalBatches,
                    totalOriginalObjects = stats.totalOriginalObjects,
                    reductionRatio = stats.reductionRatio
                };
            }
            
            if (monitorNavigation && navigationSystem != null)
            {
                var stats = navigationSystem.GetStatistics();
                currentSample.navigationStats = new NavigationStats
                {
                    totalVertices = stats.totalVertices,
                    totalTriangles = stats.totalTriangles,
                    dynamicObstacles = stats.dynamicObstacles
                };
            }
            
            if (monitorReflections && reflectionSystem != null)
            {
                var stats = reflectionSystem.GetStatistics();
                currentSample.reflectionStats = new ReflectionStats
                {
                    totalProbes = stats.totalProbes,
                    dynamicProbes = stats.dynamicProbes
                };
            }
            
            // 保存样本
            performanceSamples.Enqueue(currentSample);
            if (performanceSamples.Count > sampleCount)
            {
                performanceSamples.Dequeue();
            }
            
            // 重置计数器
            frameCount = 0;
            accumulatedFrameTime = 0;
        }
        
        /// <summary>
        /// 分析性能
        /// </summary>
        private void AnalyzePerformance()
        {
            PerformanceIssue issues = CheckForPerformanceIssues();
            
            if (issues.HasIssues && logWarnings)
            {
                LogPerformanceIssues(issues);
            }
            
            // 触发性能事件
            OnPerformanceAnalyzed?.Invoke(currentSample, issues);
        }
        
        /// <summary>
        /// 检查性能问题
        /// </summary>
        private PerformanceIssue CheckForPerformanceIssues()
        {
            PerformanceIssue issues = new PerformanceIssue();
            
            // 检查FPS
            if (currentSample.fps < thresholds.criticalFPS)
            {
                issues.fpsLevel = IssueLevel.Critical;
                issues.issues.Add($"FPS严重过低: {currentSample.fps:F1} (临界值: {thresholds.criticalFPS})");
            }
            else if (currentSample.fps < thresholds.warningFPS)
            {
                issues.fpsLevel = IssueLevel.Warning;
                issues.issues.Add($"FPS警告: {currentSample.fps:F1} (警告值: {thresholds.warningFPS})");
            }
            
            // 检查帧时间
            if (currentSample.frameTime > thresholds.criticalFrameTime)
            {
                issues.frameTimeLevel = IssueLevel.Critical;
                issues.issues.Add($"帧时间严重过长: {currentSample.frameTime:F2}ms (临界值: {thresholds.criticalFrameTime}ms)");
            }
            else if (currentSample.frameTime > thresholds.warningFrameTime)
            {
                issues.frameTimeLevel = IssueLevel.Warning;
                issues.issues.Add($"帧时间警告: {currentSample.frameTime:F2}ms (警告值: {thresholds.warningFrameTime}ms)");
            }
            
            // 检查绘制调用
            if (currentSample.drawCalls > thresholds.criticalDrawCalls)
            {
                issues.drawCallLevel = IssueLevel.Critical;
                issues.issues.Add($"绘制调用过多: {currentSample.drawCalls} (临界值: {thresholds.criticalDrawCalls})");
            }
            else if (currentSample.drawCalls > thresholds.warningDrawCalls)
            {
                issues.drawCallLevel = IssueLevel.Warning;
                issues.issues.Add($"绘制调用警告: {currentSample.drawCalls} (警告值: {thresholds.warningDrawCalls})");
            }
            
            // 检查内存使用
            if (currentSample.memoryUsage > 1024) // 超过1GB
            {
                issues.memoryLevel = IssueLevel.Warning;
                issues.issues.Add($"内存使用较高: {currentSample.memoryUsage:F1}MB");
            }
            
            return issues;
        }
        
        /// <summary>
        /// 记录性能问题
        /// </summary>
        private void LogPerformanceIssues(PerformanceIssue issues)
        {
            StringBuilder logMessage = new StringBuilder();
            logMessage.AppendLine("性能问题检测:");
            
            foreach (string issue in issues.issues)
            {
                logMessage.AppendLine($"  - {issue}");
            }
            
            if (issues.issues.Count > 0)
            {
                Debug.LogWarning(logMessage.ToString());
            }
        }
        
        /// <summary>
        /// 应用自动优化
        /// </summary>
        private void ApplyAutoOptimizations()
        {
            PerformanceIssue issues = CheckForPerformanceIssues();
            
            if (issues.fpsLevel == IssueLevel.Critical || issues.frameTimeLevel == IssueLevel.Critical)
            {
                ApplyAggressiveOptimizations();
            }
            else if (issues.fpsLevel == IssueLevel.Warning || issues.frameTimeLevel == IssueLevel.Warning)
            {
                ApplyModerateOptimizations();
            }
        }
        
        /// <summary>
        /// 应用激进优化
        /// </summary>
        private void ApplyAggressiveOptimizations()
        {
            Debug.Log("应用激进优化策略");
            
            // 降低渲染质量
            QualitySettings.SetQualityLevel(Mathf.Max(0, QualitySettings.GetQualityLevel() - 2), true);
            
            // 减少阴影距离
            QualitySettings.shadowDistance = Mathf.Max(10.0f, QualitySettings.shadowDistance * 0.5f);
            
            // 禁用实时反射
            if (reflectionSystem != null)
            {
                // 这里应该调用反射系统的方法来禁用动态更新
            }
            
            // 增加遮挡剔除距离
            if (occlusionSystem != null)
            {
                // 这里应该调用遮挡系统的方法来调整参数
            }
        }
        
        /// <summary>
        /// 应用适度优化
        /// </summary>
        private void ApplyModerateOptimizations()
        {
            Debug.Log("应用适度优化策略");
            
            // 稍微降低渲染质量
            if (QualitySettings.GetQualityLevel() > 0)
            {
                QualitySettings.SetQualityLevel(QualitySettings.GetQualityLevel() - 1, true);
            }
            
            // 减少一些特效
            // 这里可以根据需要添加更多优化策略
        }
        
        /// <summary>
        /// 获取性能报告
        /// </summary>
        public PerformanceReport GetPerformanceReport()
        {
            if (performanceSamples.Count == 0)
            {
                return new PerformanceReport();
            }
            
            // 计算统计数据
            float avgFPS = performanceSamples.Average(s => s.fps);
            float minFPS = performanceSamples.Min(s => s.fps);
            float maxFPS = performanceSamples.Max(s => s.fps);
            
            float avgFrameTime = performanceSamples.Average(s => s.frameTime);
            float maxFrameTime = performanceSamples.Max(s => s.frameTime);
            
            int avgDrawCalls = (int)performanceSamples.Average(s => s.drawCalls);
            int maxDrawCalls = performanceSamples.Max(s => s.drawCalls);
            
            float avgMemory = performanceSamples.Average(s => s.memoryUsage);
            float maxMemory = performanceSamples.Max(s => s.memoryUsage);
            
            return new PerformanceReport
            {
                averageFPS = avgFPS,
                minimumFPS = minFPS,
                maximumFPS = maxFPS,
                averageFrameTime = avgFrameTime,
                maximumFrameTime = maxFrameTime,
                averageDrawCalls = avgDrawCalls,
                maximumDrawCalls = maxDrawCalls,
                averageMemoryUsage = avgMemory,
                maximumMemoryUsage = maxMemory,
                sampleCount = performanceSamples.Count,
                timestamp = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
            };
        }
        
        /// <summary>
        /// 导出性能数据
        /// </summary>
        public string ExportPerformanceData(bool includeSubsystems = true)
        {
            StringBuilder csv = new StringBuilder();
            
            // 添加标题行
            csv.AppendLine("Timestamp,FPS,FrameTime(ms),DrawCalls,Memory(MB)");
            
            // 添加数据行
            foreach (PerformanceSample sample in performanceSamples)
            {
                csv.AppendLine($"{sample.timestamp:F2},{sample.fps:F2},{sample.frameTime:F2},{sample.drawCalls},{sample.memoryUsage:F1}");
            }
            
            return csv.ToString();
        }
        
        /// <summary>
        /// 性能分析完成事件
        /// </summary>
        public event System.Action<PerformanceSample, PerformanceIssue> OnPerformanceAnalyzed;
        
        /// <summary>
        /// 性能问题结构
        /// </summary>
        public struct PerformanceIssue
        {
            public IssueLevel fpsLevel;
            public IssueLevel frameTimeLevel;
            public IssueLevel drawCallLevel;
            public IssueLevel memoryLevel;
            public List<string> issues;
            
            public bool HasIssues => issues.Count > 0;
        }
        
        /// <summary>
        /// 问题等级
        /// </summary>
        public enum IssueLevel
        {
            Normal,
            Warning,
            Critical
        }
        
        /// <summary>
        /// 性能报告结构
        /// </summary>
        public struct PerformanceReport
        {
            public float averageFPS;
            public float minimumFPS;
            public float maximumFPS;
            public float averageFrameTime;
            public float maximumFrameTime;
            public int averageDrawCalls;
            public int maximumDrawCalls;
            public float averageMemoryUsage;
            public float maximumMemoryUsage;
            public int sampleCount;
            public string timestamp;
        }
        
        /// <summary>
        /// 子系统统计结构
        /// </summary>
        public struct LightingStats
        {
            // 光照系统统计信息
        }
        
        public struct OcclusionStats
        {
            public int totalObjects;
            public int visibleObjects;
            public float cullingRatio;
        }
        
        public struct BatchingStats
        {
            public int totalBatches;
            public int totalOriginalObjects;
            public float reductionRatio;
        }
        
        public struct NavigationStats
        {
            public int totalVertices;
            public int totalTriangles;
            public int dynamicObstacles;
        }
        
        public struct ReflectionStats
        {
            public int totalProbes;
            public int dynamicProbes;
        }
    }
}

总结

本章详细介绍了Unity中静态对象优化与导航系统的关键技术,包括光照烘焙、遮挡剔除、静态批处理、导航网格和反射探头等系统。每个部分都从数学原理出发,结合实际商业项目需求,提供了完整的C#实现代码。

关键技术要点总结:

  1. 光照烘焙优化:通过预计算光照信息,大幅减少实时渲染开销。在商业项目中需要支持动态切换和混合光照模式。

  2. 遮挡剔除系统:基于视锥体裁剪和层次深度缓冲,有效减少不可见对象的渲染。事件驱动的架构支持游戏逻辑与渲染优化的紧密结合。

  3. 批处理技术:静态批处理用于合并静态网格,动态批处理智能管理系统根据对象属性和距离动态合并移动对象。

  4. 导航网格系统:基于A*算法和网格生成技术,支持动态障碍和路径平滑,提供自然的角色移动。

  5. 反射探头管理:通过探头混合和动态更新,提供高质量的局部反射效果,特别是对于金属和光滑材质。

  6. 综合性能监控:实时监控各子系统性能,自动应用优化策略,确保游戏在各种硬件上都能流畅运行。

这些技术在实际商业项目中需要根据具体需求进行调整和优化。例如,在开放世界游戏中可能需要更激进的遮挡剔除,而在室内场景中则需要更精细的光照烘焙。通过合理配置和优化这些系统,可以显著提升游戏的性能和视觉质量。

所有代码示例都遵循Allman风格和驼峰命名法,确保在Unity 2021.3.8f1c1和VS2022/VSCODE中能够正确运行。实际项目中还需要考虑内存管理、多线程安全和平台差异等因素,这些实现提供了坚实的基础框架和优化思路。

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐