第5章 Unity导航网格系统高级应用
第5章 Unity导航网格系统高级应用
5.1 导航网格系统概述
导航网格(NavMesh)是现代游戏AI导航系统的核心技术,它通过将可行走区域转换为多边形网格,为AI角色提供智能路径规划的基础。Unity引擎的NavMesh系统经过多年发展,在2021版本中提供了更强大、更灵活的API,使开发者能够实现复杂的导航需求。
导航网格的核心原理是将3D游戏场景中的可行走表面转换为2.5D的三角形网格表示。这种转换允许AI系统快速计算两点之间的可行走路径,同时避开障碍物和不可行走区域。与传统基于路点的导航系统相比,导航网格具有以下优势:
- 动态适应性:能够处理复杂地形和动态变化的场景
- 路径质量:生成平滑、自然的移动路径
- 性能优化:预计算导航数据,运行时快速查询
- 多代理支持:支持不同类型角色的差异化导航需求
在商业游戏项目中,导航网格系统通常用于实现以下功能:
- NPC的寻路和移动
- 敌人的追击和包围行为
- 玩家的自动寻路辅助
- 群体行为的路径规划
- 动态环境的自适应导航
5.2 配置与初始化导航网格开发环境
在开始使用Unity的导航网格系统之前,需要进行正确的环境配置和初始化设置。这一过程包括导入必要的包、配置项目设置以及建立开发工作流程。
5.2.1 导航系统包导入与配置
Unity 2021.3.8f1c1中,导航系统通过两个主要包提供:AI Navigation和AI Navigation Components。以下是详细的配置步骤:
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
namespace NavigationSystem
{
// 导航系统初始化管理器
public class NavigationSystemInitializer : MonoBehaviour
{
[Header("导航系统设置")]
[SerializeField] private bool autoInitialize = true;
[SerializeField] private NavigationSystemSettings systemSettings;
private NavMeshData navMeshData;
private List<NavMeshBuildSource> buildSources;
private NavMeshBuildSettings buildSettings;
private void Awake()
{
if (autoInitialize)
{
InitializeNavigationSystem();
}
}
public void InitializeNavigationSystem()
{
Debug.Log("开始初始化导航系统...");
// 步骤1:检查导航系统可用性
if (!IsNavigationSystemAvailable())
{
Debug.LogError("导航系统不可用,请确保已导入AI Navigation包");
return;
}
// 步骤2:加载或创建导航系统设置
LoadNavigationSettings();
// 步骤3:收集场景中的导航几何体
CollectNavigationGeometry();
// 步骤4:配置代理类型
ConfigureAgentTypes();
// 步骤5:构建初始导航网格
BuildInitialNavMesh();
Debug.Log("导航系统初始化完成");
}
private bool IsNavigationSystemAvailable()
{
// 检查必要的组件和类型是否可用
try
{
var testAgent = new NavMeshAgent();
return true;
}
catch (System.Exception)
{
return false;
}
}
private void LoadNavigationSettings()
{
// 如果未提供设置,创建默认设置
if (systemSettings == null)
{
systemSettings = ScriptableObject.CreateInstance<NavigationSystemSettings>();
ConfigureDefaultSettings(systemSettings);
Debug.Log("使用默认导航系统设置");
}
// 应用设置到导航系统
ApplySystemSettings();
}
private void ConfigureDefaultSettings(NavigationSystemSettings settings)
{
settings.defaultAgentTypeID = 0;
settings.buildHeightMesh = true;
settings.autoRebuild = true;
settings.rebuildInterval = 0.5f;
settings.asyncBuild = true;
// 默认代理设置
settings.defaultAgentRadius = 0.5f;
settings.defaultAgentHeight = 2.0f;
settings.defaultAgentStepHeight = 0.4f;
settings.defaultMaxSlope = 45.0f;
}
private void ApplySystemSettings()
{
// 配置全局导航网格设置
NavMesh.pathfindingIterationsPerFrame = systemSettings.pathfindingIterationsPerFrame;
NavMesh.avoidancePredictionTime = systemSettings.avoidancePredictionTime;
// 设置自动重建
if (systemSettings.autoRebuild)
{
StartCoroutine(AutoRebuildNavMesh());
}
}
private System.Collections.IEnumerator AutoRebuildNavMesh()
{
while (true)
{
yield return new WaitForSeconds(systemSettings.rebuildInterval);
if (NavMeshSurface.activeSurfaces.Count > 0)
{
NavMeshSurface.UpdateActive();
}
}
}
private void CollectNavigationGeometry()
{
buildSources = new List<NavMeshBuildSource>();
// 收集所有带有NavMeshSourceTag的几何体
NavMeshSourceTag[] sourceTags = FindObjectsOfType<NavMeshSourceTag>();
foreach (NavMeshSourceTag tag in sourceTags)
{
buildSources.AddRange(tag.Collect());
}
// 如果没有显式标记,收集所有带有MeshFilter的静态几何体
if (buildSources.Count == 0)
{
CollectStaticGeometry();
}
Debug.Log($"收集到 {buildSources.Count} 个导航网格源");
}
private void CollectStaticGeometry()
{
MeshFilter[] meshFilters = FindObjectsOfType<MeshFilter>();
foreach (MeshFilter filter in meshFilters)
{
// 只处理标记为静态的对象
if (filter.gameObject.isStatic && filter.sharedMesh != null)
{
NavMeshBuildSource source = new NavMeshBuildSource
{
shape = NavMeshBuildSourceShape.Mesh,
sourceObject = filter.sharedMesh,
transform = filter.transform.localToWorldMatrix,
area = 0 // 可行走区域
};
buildSources.Add(source);
}
}
}
private void ConfigureAgentTypes()
{
// 获取或创建代理类型设置
var agentTypes = NavMesh.GetSettingsByIndex(0);
// 配置默认人类代理
if (agentTypes.agentTypeID == -1)
{
CreateDefaultAgentType();
}
// 配置多个代理类型(人类、车辆、飞行器等)
ConfigureMultipleAgentTypes();
}
private void CreateDefaultAgentType()
{
NavMeshBuildSettings settings = new NavMeshBuildSettings
{
agentTypeID = 0,
agentRadius = systemSettings.defaultAgentRadius,
agentHeight = systemSettings.defaultAgentHeight,
agentSlope = systemSettings.defaultMaxSlope,
agentClimb = systemSettings.defaultAgentStepHeight,
minRegionArea = 2.0f,
tileSize = 256
};
NavMesh.AddSettings(settings);
}
private void ConfigureMultipleAgentTypes()
{
// 人类代理(默认)
NavMeshBuildSettings humanSettings = NavMesh.CreateSettings();
humanSettings.agentTypeID = 0;
humanSettings.agentRadius = 0.5f;
humanSettings.agentHeight = 2.0f;
humanSettings.agentSlope = 45.0f;
humanSettings.agentClimb = 0.4f;
humanSettings.agentTypeID = 0;
// 小型代理(如老鼠、昆虫)
NavMeshBuildSettings smallSettings = NavMesh.CreateSettings();
smallSettings.agentTypeID = 1;
smallSettings.agentRadius = 0.1f;
smallSettings.agentHeight = 0.5f;
smallSettings.agentSlope = 60.0f;
smallSettings.agentClimb = 0.2f;
// 大型代理(如巨人、车辆)
NavMeshBuildSettings largeSettings = NavMesh.CreateSettings();
largeSettings.agentTypeID = 2;
largeSettings.agentRadius = 1.0f;
largeSettings.agentHeight = 3.0f;
largeSettings.agentSlope = 30.0f;
largeSettings.agentClimb = 0.6f;
// 添加到导航系统
NavMesh.AddSettings(humanSettings);
NavMesh.AddSettings(smallSettings);
NavMesh.AddSettings(largeSettings);
}
private void BuildInitialNavMesh()
{
if (buildSources == null || buildSources.Count == 0)
{
Debug.LogWarning("没有找到导航网格源,跳过初始构建");
return;
}
// 使用默认代理设置
buildSettings = NavMesh.CreateSettings();
buildSettings.agentTypeID = 0;
// 定义导航网格的边界(整个场景)
Bounds bounds = CalculateSceneBounds();
// 异步构建导航网格
if (systemSettings.asyncBuild)
{
NavMeshBuilder.UpdateNavMeshDataAsync(
navMeshData,
buildSettings,
buildSources,
bounds
);
}
else
{
navMeshData = NavMeshBuilder.BuildNavMeshData(
buildSettings,
buildSources,
bounds,
Vector3.zero,
Quaternion.identity
);
NavMesh.AddNavMeshData(navMeshData);
}
}
private Bounds CalculateSceneBounds()
{
// 计算包含所有导航源的边界
Bounds bounds = new Bounds();
bool hasBounds = false;
foreach (NavMeshBuildSource source in buildSources)
{
Bounds sourceBounds = CalculateSourceBounds(source);
if (!hasBounds)
{
bounds = sourceBounds;
hasBounds = true;
}
else
{
bounds.Encapsulate(sourceBounds);
}
}
// 如果没有找到边界,使用默认值
if (!hasBounds)
{
bounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
}
// 扩大边界以确保完全包含
bounds.Expand(10.0f);
return bounds;
}
private Bounds CalculateSourceBounds(NavMeshBuildSource source)
{
switch (source.shape)
{
case NavMeshBuildSourceShape.Mesh:
Mesh mesh = source.sourceObject as Mesh;
if (mesh != null)
{
Bounds meshBounds = mesh.bounds;
// 转换到世界空间
Vector3 min = source.transform.MultiplyPoint(meshBounds.min);
Vector3 max = source.transform.MultiplyPoint(meshBounds.max);
return new Bounds((min + max) * 0.5f, max - min);
}
break;
case NavMeshBuildSourceShape.Terrain:
TerrainData terrain = source.sourceObject as TerrainData;
if (terrain != null)
{
Bounds terrainBounds = terrain.bounds;
Vector3 min = source.transform.MultiplyPoint(terrainBounds.min);
Vector3 max = source.transform.MultiplyPoint(terrainBounds.max);
return new Bounds((min + max) * 0.5f, max - min);
}
break;
case NavMeshBuildSourceShape.Box:
case NavMeshBuildSourceShape.Sphere:
case NavMeshBuildSourceShape.Capsule:
case NavMeshBuildSourceShape.ModifierBox:
// 对于基本形状,使用转换后的局部边界
Matrix4x4 matrix = source.transform;
Vector3 center = matrix.GetColumn(3);
Vector3 size = matrix.lossyScale;
return new Bounds(center, size);
}
return new Bounds(Vector3.zero, Vector3.zero);
}
private void OnDestroy()
{
// 清理导航网格数据
if (navMeshData != null)
{
NavMesh.RemoveNavMeshData(navMeshData);
}
}
}
// 导航系统设置数据类
[CreateAssetMenu(fileName = "NavigationSystemSettings", menuName = "AI/Navigation System Settings")]
public class NavigationSystemSettings : ScriptableObject
{
[Header("常规设置")]
public int defaultAgentTypeID = 0;
public bool buildHeightMesh = true;
public bool autoRebuild = true;
public float rebuildInterval = 0.5f;
public bool asyncBuild = true;
[Header("默认代理参数")]
public float defaultAgentRadius = 0.5f;
public float defaultAgentHeight = 2.0f;
public float defaultAgentStepHeight = 0.4f;
public float defaultMaxSlope = 45.0f;
[Header("性能设置")]
public int pathfindingIterationsPerFrame = 100;
public float avoidancePredictionTime = 2.0f;
public int maxNavMeshNodes = 1024;
[Header("高级设置")]
public float cellSize = 0.1666667f;
public float cellHeight = 0.1f;
public float maxEdgeLength = 12.0f;
public float maxEdgeError = 1.0f;
public float detailSampleDistance = 6.0f;
public float detailSampleMaxError = 1.0f;
}
// 导航源标签组件
public class NavMeshSourceTag : MonoBehaviour
{
[Header("源设置")]
public int areaType = 0; // 0=可行走,1=不可行走,2=跳跃,3=爬梯等
public bool includeInNavMesh = true;
public bool isWalkable = true;
[Header("几何体设置")]
public Mesh sourceMesh;
public NavMeshBuildSourceShape shape = NavMeshBuildSourceShape.Mesh;
public Vector3 modifierSize = Vector3.one;
public List<NavMeshBuildSource> Collect()
{
List<NavMeshBuildSource> sources = new List<NavMeshBuildSource>();
if (!includeInNavMesh)
{
return sources;
}
NavMeshBuildSource source = new NavMeshBuildSource();
source.area = areaType;
// 根据形状类型设置源
switch (shape)
{
case NavMeshBuildSourceShape.Mesh:
if (sourceMesh != null)
{
source.shape = NavMeshBuildSourceShape.Mesh;
source.sourceObject = sourceMesh;
source.transform = transform.localToWorldMatrix;
sources.Add(source);
}
break;
case NavMeshBuildSourceShape.Box:
source.shape = NavMeshBuildSourceShape.Box;
source.size = modifierSize;
source.transform = transform.localToWorldMatrix;
sources.Add(source);
break;
case NavMeshBuildSourceShape.Sphere:
source.shape = NavMeshBuildSourceShape.Sphere;
source.size = Vector3.one * modifierSize.x;
source.transform = transform.localToWorldMatrix;
sources.Add(source);
break;
case NavMeshBuildSourceShape.Capsule:
source.shape = NavMeshBuildSourceShape.Capsule;
source.size = new Vector3(modifierSize.x, modifierSize.y, 0);
source.transform = transform.localToWorldMatrix;
sources.Add(source);
break;
}
return sources;
}
private void OnDrawGizmosSelected()
{
// 在编辑器中选择时显示导航源范围
Gizmos.color = isWalkable ? Color.green : Color.red;
switch (shape)
{
case NavMeshBuildSourceShape.Mesh:
if (sourceMesh != null)
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireMesh(sourceMesh);
}
break;
case NavMeshBuildSourceShape.Box:
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(Vector3.zero, modifierSize);
break;
case NavMeshBuildSourceShape.Sphere:
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireSphere(Vector3.zero, modifierSize.x);
break;
case NavMeshBuildSourceShape.Capsule:
Gizmos.matrix = transform.localToWorldMatrix;
DrawWireCapsule(Vector3.zero, modifierSize.x, modifierSize.y);
break;
}
}
private void DrawWireCapsule(Vector3 center, float radius, float height)
{
// 绘制胶囊体线框
Vector3 up = Vector3.up * (height - radius * 2) * 0.5f;
// 绘制中间圆柱部分
Gizmos.DrawWireSphere(center + up, radius);
Gizmos.DrawWireSphere(center - up, radius);
// 绘制侧面线
for (int i = 0; i < 360; i += 45)
{
float angle = i * Mathf.Deg2Rad;
Vector3 point = new Vector3(Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius);
Gizmos.DrawLine(center + up + point, center - up + point);
}
}
}
}
5.2.2 开发环境优化配置
在商业项目中,导航网格系统的性能至关重要。以下是一些优化配置建议:
- 分层构建策略:根据地形复杂度使用不同的体素大小
- 异步处理:避免在主线程执行耗时的导航计算
- 内存管理:合理管理导航网格数据的生命周期
- 调试工具:开发可视化调试工具,便于问题排查
// 导航系统性能优化管理器
public class NavigationPerformanceOptimizer : MonoBehaviour
{
[System.Serializable]
public class OptimizationProfile
{
public string profileName;
public float rebuildInterval = 1.0f;
public int maxSimultaneousPathfinds = 10;
public bool useSimplifiedPathfinding = false;
public float pathSimplificationTolerance = 0.5f;
public bool cachePaths = true;
public int maxCachedPaths = 100;
}
public OptimizationProfile[] profiles;
public string currentProfileName = "Default";
private Dictionary<string, OptimizationProfile> profileLookup;
private Queue<NavMeshPath> pathCache;
private int activePathfinds = 0;
private void Start()
{
InitializeProfiles();
InitializePathCache();
ApplyProfile(currentProfileName);
}
private void InitializeProfiles()
{
profileLookup = new Dictionary<string, OptimizationProfile>();
// 创建默认配置文件
if (profiles.Length == 0)
{
profiles = new OptimizationProfile[]
{
new OptimizationProfile
{
profileName = "Default",
rebuildInterval = 1.0f,
maxSimultaneousPathfinds = 10
},
new OptimizationProfile
{
profileName = "Performance",
rebuildInterval = 2.0f,
maxSimultaneousPathfinds = 5,
useSimplifiedPathfinding = true,
pathSimplificationTolerance = 1.0f
},
new OptimizationProfile
{
profileName = "Quality",
rebuildInterval = 0.5f,
maxSimultaneousPathfinds = 20,
useSimplifiedPathfinding = false
}
};
}
foreach (OptimizationProfile profile in profiles)
{
profileLookup[profile.profileName] = profile;
}
}
private void InitializePathCache()
{
pathCache = new Queue<NavMeshPath>();
// 预创建路径对象,减少GC
for (int i = 0; i < 20; i++)
{
pathCache.Enqueue(new NavMeshPath());
}
}
public void ApplyProfile(string profileName)
{
if (profileLookup.ContainsKey(profileName))
{
OptimizationProfile profile = profileLookup[profileName];
currentProfileName = profileName;
// 应用性能设置
NavMesh.pathfindingIterationsPerFrame = profile.maxSimultaneousPathfinds * 10;
Debug.Log($"应用导航性能配置: {profileName}");
}
}
public NavMeshPath GetCachedPath()
{
if (pathCache.Count > 0)
{
return pathCache.Dequeue();
}
// 缓存为空时创建新路径
return new NavMeshPath();
}
public void ReturnPathToCache(NavMeshPath path)
{
if (pathCache.Count < 100) // 限制缓存大小
{
path.ClearCorners();
pathCache.Enqueue(path);
}
}
public bool CanStartPathfind()
{
if (profileLookup.ContainsKey(currentProfileName))
{
OptimizationProfile profile = profileLookup[currentProfileName];
return activePathfinds < profile.maxSimultaneousPathfinds;
}
return true;
}
public void StartPathfind()
{
activePathfinds++;
}
public void EndPathfind()
{
activePathfinds = Mathf.Max(0, activePathfinds - 1);
}
// 简化的路径查找方法
public NavMeshPath FindSimplifiedPath(Vector3 source, Vector3 target)
{
if (!CanStartPathfind())
{
return null;
}
StartPathfind();
NavMeshPath path = GetCachedPath();
try
{
if (NavMesh.CalculatePath(source, target, NavMesh.AllAreas, path))
{
if (profileLookup[currentProfileName].useSimplifiedPathfinding)
{
path = SimplifyPath(path);
}
}
}
finally
{
EndPathfind();
}
return path;
}
private NavMeshPath SimplifyPath(NavMeshPath originalPath)
{
if (originalPath.corners.Length <= 2)
{
return originalPath;
}
List<Vector3> simplified = new List<Vector3>();
simplified.Add(originalPath.corners[0]);
float tolerance = profileLookup[currentProfileName].pathSimplificationTolerance;
for (int i = 1; i < originalPath.corners.Length - 1; i++)
{
Vector3 lastAdded = simplified[simplified.Count - 1];
Vector3 current = originalPath.corners[i];
Vector3 next = originalPath.corners[i + 1];
// 检查当前点是否可以跳过
Vector3 lineDir = (next - lastAdded).normalized;
float distance = Vector3.Cross(lineDir, current - lastAdded).magnitude;
if (distance > tolerance)
{
simplified.Add(current);
}
}
simplified.Add(originalPath.corners[originalPath.corners.Length - 1]);
NavMeshPath simplifiedPath = GetCachedPath();
simplifiedPath.corners = simplified.ToArray();
return simplifiedPath;
}
}
5.3 多类型代理的导航网格构建与管理
在实际游戏项目中,不同AI角色通常具有不同的移动特性。人类角色、动物、车辆、飞行器等都需要不同的导航参数。Unity的导航网格系统支持为不同类型的代理创建独立的导航数据。
5.3.1 多代理类型配置与管理
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
namespace MultiAgentNavigation
{
// 代理类型定义
public enum AgentCategory
{
Humanoid,
Vehicle,
Animal,
Flying,
Swarm
}
// 代理类型配置
[System.Serializable]
public class AgentTypeConfiguration
{
public string typeName;
public AgentCategory category;
public int agentTypeID;
[Header("物理参数")]
public float radius = 0.5f;
public float height = 2.0f;
public float stepHeight = 0.4f;
public float maxSlope = 45.0f;
[Header("移动参数")]
public float speed = 3.5f;
public float angularSpeed = 120f;
public float acceleration = 8f;
public float stoppingDistance = 0.5f;
[Header("避障参数")]
public float avoidanceRadius = 1.0f;
public float avoidancePriority = 50f;
public LayerMask avoidanceMask = -1;
[Header("高级设置")]
public bool autoTraverseOffMeshLink = true;
public bool autoRepath = true;
public float autoRepathDistance = 1.0f;
}
// 多代理导航管理器
public class MultiAgentNavigationManager : MonoBehaviour
{
[Header("代理类型配置")]
public List<AgentTypeConfiguration> agentConfigurations;
[Header("导航网格设置")]
public bool buildSeparateNavMeshes = true;
public float navMeshBuildMargin = 5.0f;
private Dictionary<int, AgentTypeConfiguration> configurationLookup;
private Dictionary<int, NavMeshData> navMeshDataMap;
private Dictionary<int, NavMeshBuildSettings> buildSettingsMap;
private void Awake()
{
InitializeAgentConfigurations();
InitializeNavMeshData();
if (buildSeparateNavMeshes)
{
BuildMultiAgentNavMeshes();
}
}
private void InitializeAgentConfigurations()
{
configurationLookup = new Dictionary<int, AgentTypeConfiguration>();
// 确保至少有一个默认配置
if (agentConfigurations == null || agentConfigurations.Count == 0)
{
CreateDefaultConfigurations();
}
// 建立查找表
foreach (AgentTypeConfiguration config in agentConfigurations)
{
configurationLookup[config.agentTypeID] = config;
// 确保代理类型ID在Unity中注册
RegisterAgentType(config);
}
}
private void CreateDefaultConfigurations()
{
agentConfigurations = new List<AgentTypeConfiguration>();
// 人类代理
agentConfigurations.Add(new AgentTypeConfiguration
{
typeName = "Human",
category = AgentCategory.Humanoid,
agentTypeID = 0,
radius = 0.5f,
height = 2.0f,
stepHeight = 0.4f,
maxSlope = 45.0f,
speed = 3.5f
});
// 车辆代理
agentConfigurations.Add(new AgentTypeConfiguration
{
typeName = "Vehicle",
category = AgentCategory.Vehicle,
agentTypeID = 1,
radius = 1.0f,
height = 2.5f,
stepHeight = 0.6f,
maxSlope = 30.0f,
speed = 8.0f
});
// 动物代理
agentConfigurations.Add(new AgentTypeConfiguration
{
typeName = "Animal",
category = AgentCategory.Animal,
agentTypeID = 2,
radius = 0.3f,
height = 1.0f,
stepHeight = 0.3f,
maxSlope = 60.0f,
speed = 5.0f
});
// 飞行代理
agentConfigurations.Add(new AgentTypeConfiguration
{
typeName = "Flying",
category = AgentCategory.Flying,
agentTypeID = 3,
radius = 0.2f,
height = 0.5f,
stepHeight = 0.1f,
maxSlope = 90.0f,
speed = 7.0f
});
}
private void RegisterAgentType(AgentTypeConfiguration config)
{
// 创建或获取代理类型的构建设置
NavMeshBuildSettings settings = NavMesh.CreateSettings();
settings.agentTypeID = config.agentTypeID;
settings.agentRadius = config.radius;
settings.agentHeight = config.height;
settings.agentSlope = config.maxSlope;
settings.agentClimb = config.stepHeight;
// 添加到导航系统
NavMesh.AddSettings(settings);
if (buildSettingsMap == null)
{
buildSettingsMap = new Dictionary<int, NavMeshBuildSettings>();
}
buildSettingsMap[config.agentTypeID] = settings;
}
private void InitializeNavMeshData()
{
navMeshDataMap = new Dictionary<int, NavMeshData>();
}
private void BuildMultiAgentNavMeshes()
{
// 为每个代理类型构建独立的导航网格
foreach (AgentTypeConfiguration config in agentConfigurations)
{
BuildNavMeshForAgentType(config.agentTypeID);
}
}
private void BuildNavMeshForAgentType(int agentTypeID)
{
if (!configurationLookup.ContainsKey(agentTypeID))
{
Debug.LogWarning($"未找到代理类型ID {agentTypeID} 的配置");
return;
}
if (!buildSettingsMap.ContainsKey(agentTypeID))
{
Debug.LogWarning($"未找到代理类型ID {agentTypeID} 的构建设置");
return;
}
// 收集适合该代理类型的导航源
List<NavMeshBuildSource> sources = CollectSourcesForAgentType(agentTypeID);
if (sources.Count == 0)
{
Debug.LogWarning($"没有找到适合代理类型 {agentTypeID} 的导航源");
return;
}
// 计算边界
Bounds bounds = CalculateBoundsForSources(sources);
bounds.Expand(navMeshBuildMargin);
// 构建导航网格数据
NavMeshBuildSettings settings = buildSettingsMap[agentTypeID];
NavMeshData navMeshData = NavMeshBuilder.BuildNavMeshData(
settings,
sources,
bounds,
Vector3.zero,
Quaternion.identity
);
if (navMeshData != null)
{
// 移除旧的导航网格数据(如果存在)
if (navMeshDataMap.ContainsKey(agentTypeID) && navMeshDataMap[agentTypeID] != null)
{
NavMesh.RemoveNavMeshData(navMeshDataMap[agentTypeID]);
}
// 添加新的导航网格数据
NavMesh.AddNavMeshData(navMeshData);
navMeshDataMap[agentTypeID] = navMeshData;
Debug.Log($"为代理类型 {configurationLookup[agentTypeID].typeName} 构建导航网格完成");
}
}
private List<NavMeshBuildSource> CollectSourcesForAgentType(int agentTypeID)
{
List<NavMeshBuildSource> filteredSources = new List<NavMeshBuildSource>();
// 收集所有导航源
NavMeshSourceTag[] allTags = FindObjectsOfType<NavMeshSourceTag>();
foreach (NavMeshSourceTag tag in allTags)
{
// 根据代理类型过滤源
if (IsSourceSuitableForAgent(tag, agentTypeID))
{
filteredSources.AddRange(tag.Collect());
}
}
return filteredSources;
}
private bool IsSourceSuitableForAgent(NavMeshSourceTag source, int agentTypeID)
{
if (!configurationLookup.ContainsKey(agentTypeID))
{
return false;
}
AgentTypeConfiguration config = configurationLookup[agentTypeID];
// 根据代理类别和源属性进行过滤
switch (config.category)
{
case AgentCategory.Humanoid:
// 人类可以行走在大部分表面上
return source.areaType == 0 && source.isWalkable;
case AgentCategory.Vehicle:
// 车辆需要平坦的道路
return source.areaType == 0 && source.isWalkable;
case AgentCategory.Animal:
// 动物可以行走在更多类型的地形上
return source.areaType <= 2 && source.isWalkable;
case AgentCategory.Flying:
// 飞行器可以穿越大部分区域,除了固体障碍物
return source.areaType != 1; // 不是不可行走区域
case AgentCategory.Swarm:
// 群体可以穿越非常小的空间
return true;
default:
return source.areaType == 0;
}
}
private Bounds CalculateBoundsForSources(List<NavMeshBuildSource> sources)
{
Bounds bounds = new Bounds();
bool hasBounds = false;
foreach (NavMeshBuildSource source in sources)
{
Bounds sourceBounds = CalculateSourceBounds(source);
if (!hasBounds)
{
bounds = sourceBounds;
hasBounds = true;
}
else
{
bounds.Encapsulate(sourceBounds);
}
}
if (!hasBounds)
{
bounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
}
return bounds;
}
private Bounds CalculateSourceBounds(NavMeshBuildSource source)
{
// 与之前相同的边界计算方法
// 这里简化为使用转换后的位置
Vector3 position = source.transform.GetColumn(3);
Vector3 size = Vector3.one;
if (source.shape == NavMeshBuildSourceShape.Mesh)
{
Mesh mesh = source.sourceObject as Mesh;
if (mesh != null)
{
size = mesh.bounds.size;
}
}
return new Bounds(position, size);
}
public AgentTypeConfiguration GetAgentConfiguration(int agentTypeID)
{
if (configurationLookup.ContainsKey(agentTypeID))
{
return configurationLookup[agentTypeID];
}
return null;
}
public NavMeshAgent CreateAgentForType(int agentTypeID, GameObject agentObject)
{
AgentTypeConfiguration config = GetAgentConfiguration(agentTypeID);
if (config == null)
{
Debug.LogError($"无法创建代理:未找到类型ID {agentTypeID} 的配置");
return null;
}
// 添加NavMeshAgent组件
NavMeshAgent agent = agentObject.AddComponent<NavMeshAgent>();
// 配置代理参数
agent.agentTypeID = config.agentTypeID;
agent.radius = config.radius;
agent.height = config.height;
agent.speed = config.speed;
agent.angularSpeed = config.angularSpeed;
agent.acceleration = config.acceleration;
agent.stoppingDistance = config.stoppingDistance;
agent.autoTraverseOffMeshLink = config.autoTraverseOffMeshLink;
agent.autoRepath = config.autoRepath;
// 配置避障
agent.obstacleAvoidanceType = ObstacleAvoidanceType.GoodQualityObstacleAvoidance;
agent.avoidancePriority = Mathf.RoundToInt(config.avoidancePriority);
return agent;
}
public bool IsPositionReachable(Vector3 position, int agentTypeID, float maxDistance = 1.0f)
{
NavMeshHit hit;
return NavMesh.SamplePosition(
position,
out hit,
maxDistance,
NavMesh.AllAreas
);
}
public Vector3 FindNearestReachablePoint(Vector3 position, int agentTypeID, float maxDistance = 10.0f)
{
NavMeshHit hit;
if (NavMesh.SamplePosition(position, out hit, maxDistance, NavMesh.AllAreas))
{
return hit.position;
}
return position;
}
public void UpdateNavMeshForAgentType(int agentTypeID)
{
if (buildSeparateNavMeshes)
{
BuildNavMeshForAgentType(agentTypeID);
}
else
{
Debug.LogWarning("单独代理类型的导航网格更新需要启用 buildSeparateNavMeshes");
}
}
private void OnDestroy()
{
// 清理所有导航网格数据
foreach (KeyValuePair<int, NavMeshData> entry in navMeshDataMap)
{
if (entry.Value != null)
{
NavMesh.RemoveNavMeshData(entry.Value);
}
}
}
}
// 智能代理控制器
public class IntelligentAgentController : MonoBehaviour
{
[Header("代理设置")]
public int agentTypeID = 0;
public bool autoConfigure = true;
[Header("移动行为")]
public float wanderRadius = 10f;
public float minWanderDelay = 2f;
public float maxWanderDelay = 5f;
private NavMeshAgent navAgent;
private MultiAgentNavigationManager navigationManager;
private Vector3 currentDestination;
private float wanderTimer;
private float nextWanderTime;
private void Start()
{
InitializeAgent();
if (autoConfigure)
{
StartWandering();
}
}
private void InitializeAgent()
{
// 查找导航管理器
navigationManager = FindObjectOfType<MultiAgentNavigationManager>();
if (navigationManager == null)
{
Debug.LogError("未找到 MultiAgentNavigationManager");
return;
}
// 创建并配置代理
navAgent = navigationManager.CreateAgentForType(agentTypeID, gameObject);
if (navAgent == null)
{
Debug.LogError("无法创建 NavMeshAgent");
return;
}
// 设置初始位置为可到达点
Vector3 reachablePosition = navigationManager.FindNearestReachablePoint(
transform.position,
agentTypeID
);
transform.position = reachablePosition;
navAgent.Warp(reachablePosition);
Debug.Log($"智能代理初始化完成,类型ID: {agentTypeID}");
}
private void Update()
{
if (navAgent == null || !autoConfigure)
{
return;
}
// 更新漫游计时器
wanderTimer += Time.deltaTime;
if (wanderTimer >= nextWanderTime)
{
FindNewWanderDestination();
wanderTimer = 0f;
nextWanderTime = Random.Range(minWanderDelay, maxWanderDelay);
}
// 检查是否到达目的地
if (!navAgent.pathPending && navAgent.remainingDistance <= navAgent.stoppingDistance)
{
if (!navAgent.hasPath || navAgent.velocity.sqrMagnitude == 0f)
{
// 到达目的地,重置计时器
wanderTimer = nextWanderTime - 0.1f;
}
}
}
private void StartWandering()
{
wanderTimer = 0f;
nextWanderTime = Random.Range(minWanderDelay, maxWanderDelay);
FindNewWanderDestination();
}
private void FindNewWanderDestination()
{
Vector3 randomDirection = Random.insideUnitSphere * wanderRadius;
randomDirection.y = 0; // 保持水平移动
Vector3 targetPosition = transform.position + randomDirection;
// 确保目标位置可到达
targetPosition = navigationManager.FindNearestReachablePoint(targetPosition, agentTypeID, wanderRadius);
SetDestination(targetPosition);
}
public void SetDestination(Vector3 destination)
{
if (navAgent != null && navAgent.isActiveAndEnabled)
{
currentDestination = destination;
navAgent.SetDestination(destination);
}
}
public void StopMovement()
{
if (navAgent != null)
{
navAgent.isStopped = true;
}
}
public void ResumeMovement()
{
if (navAgent != null)
{
navAgent.isStopped = false;
}
}
public bool HasReachedDestination()
{
if (navAgent == null)
{
return true;
}
return !navAgent.pathPending &&
navAgent.remainingDistance <= navAgent.stoppingDistance &&
(!navAgent.hasPath || navAgent.velocity.sqrMagnitude == 0f);
}
public NavMeshPath CalculatePathTo(Vector3 target)
{
if (navAgent == null || navigationManager == null)
{
return null;
}
return navigationManager.FindSimplifiedPath(transform.position, target);
}
private void OnDrawGizmosSelected()
{
// 绘制代理的可视化信息
if (navAgent != null)
{
// 绘制代理半径
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, navAgent.radius);
// 绘制当前路径
if (navAgent.hasPath)
{
Gizmos.color = Color.green;
Vector3[] corners = navAgent.path.corners;
for (int i = 0; i < corners.Length - 1; i++)
{
Gizmos.DrawLine(corners[i], corners[i + 1]);
Gizmos.DrawSphere(corners[i], 0.1f);
}
Gizmos.DrawSphere(corners[corners.Length - 1], 0.1f);
}
// 绘制目标位置
if (currentDestination != Vector3.zero)
{
Gizmos.color = Color.red;
Gizmos.DrawSphere(currentDestination, 0.2f);
Gizmos.DrawLine(transform.position, currentDestination);
}
}
}
}
}
5.3.2 代理群体管理与优化
在包含大量AI角色的游戏中,有效的群体管理至关重要。以下实现展示了如何优化多个代理的导航性能:
// 代理群体管理器
public class AgentCrowdManager : MonoBehaviour
{
[System.Serializable]
public class CrowdFormation
{
public string formationName;
public FormationType type;
public Vector3[] relativePositions;
public float spacing = 2.0f;
}
public enum FormationType
{
Line,
Column,
Wedge,
Vee,
Square,
Circle
}
[Header("群体设置")]
public int maxAgents = 50;
public float updateInterval = 0.1f;
public bool useFormations = true;
[Header("编队配置")]
public List<CrowdFormation> formations;
private List<IntelligentAgentController> activeAgents;
private Dictionary<int, CrowdFormation> formationLookup;
private float updateTimer;
private void Start()
{
InitializeFormations();
activeAgents = new List<IntelligentAgentController>();
updateTimer = 0f;
}
private void InitializeFormations()
{
formationLookup = new Dictionary<int, CrowdFormation>();
// 创建默认编队
if (formations == null || formations.Count == 0)
{
CreateDefaultFormations();
}
for (int i = 0; i < formations.Count; i++)
{
formationLookup[i] = formations[i];
}
}
private void CreateDefaultFormations()
{
formations = new List<CrowdFormation>();
// 线性编队
formations.Add(new CrowdFormation
{
formationName = "Line",
type = FormationType.Line,
spacing = 2.0f,
relativePositions = CalculateLineFormation(5)
});
// 楔形编队
formations.Add(new CrowdFormation
{
formationName = "Wedge",
type = FormationType.Wedge,
spacing = 2.5f,
relativePositions = CalculateWedgeFormation(5)
});
// 圆形编队
formations.Add(new CrowdFormation
{
formationName = "Circle",
type = FormationType.Circle,
spacing = 3.0f,
relativePositions = CalculateCircleFormation(8)
});
}
private Vector3[] CalculateLineFormation(int agentCount)
{
Vector3[] positions = new Vector3[agentCount];
for (int i = 0; i < agentCount; i++)
{
positions[i] = new Vector3(i * 2.0f, 0, 0);
}
return positions;
}
private Vector3[] CalculateWedgeFormation(int agentCount)
{
List<Vector3> positions = new List<Vector3>();
int rows = Mathf.CeilToInt(Mathf.Sqrt(agentCount));
for (int row = 0; row < rows; row++)
{
int agentsInRow = row + 1;
float rowOffset = row * 2.5f;
for (int col = 0; col < agentsInRow; col++)
{
float colOffset = (col - row * 0.5f) * 2.5f;
positions.Add(new Vector3(colOffset, 0, -rowOffset));
}
}
return positions.ToArray();
}
private Vector3[] CalculateCircleFormation(int agentCount)
{
Vector3[] positions = new Vector3[agentCount];
float radius = 5.0f;
for (int i = 0; i < agentCount; i++)
{
float angle = i * (360f / agentCount) * Mathf.Deg2Rad;
positions[i] = new Vector3(
Mathf.Cos(angle) * radius,
0,
Mathf.Sin(angle) * radius
);
}
return positions;
}
private void Update()
{
updateTimer += Time.deltaTime;
if (updateTimer >= updateInterval)
{
UpdateCrowdNavigation();
updateTimer = 0f;
}
}
private void UpdateCrowdNavigation()
{
if (activeAgents.Count == 0)
{
return;
}
// 分批更新代理,避免在同一帧更新所有代理
int batchSize = Mathf.CeilToInt(activeAgents.Count * 0.1f); // 每次更新10%
int startIndex = (Time.frameCount % 10) * batchSize;
for (int i = 0; i < batchSize && startIndex + i < activeAgents.Count; i++)
{
IntelligentAgentController agent = activeAgents[startIndex + i];
if (agent != null && agent.enabled)
{
UpdateAgentInCrowd(agent);
}
}
}
private void UpdateAgentInCrowd(IntelligentAgentController agent)
{
// 实现群体行为逻辑
if (useFormations && activeAgents.Count > 1)
{
ApplyFormationBehavior(agent);
}
else
{
ApplyIndividualBehavior(agent);
}
}
private void ApplyFormationBehavior(IntelligentAgentController agent)
{
// 根据代理在群体中的位置应用编队行为
int agentIndex = activeAgents.IndexOf(agent);
if (agentIndex >= 0)
{
// 选择编队(基于群体大小)
int formationIndex = Mathf.Min(agentIndex / 5, formations.Count - 1);
if (formationLookup.ContainsKey(formationIndex))
{
CrowdFormation formation = formationLookup[formationIndex];
// 计算编队中的目标位置
Vector3 formationTarget = CalculateFormationPosition(agentIndex, formation);
// 设置目标
agent.SetDestination(formationTarget);
}
}
}
private Vector3 CalculateFormationPosition(int agentIndex, CrowdFormation formation)
{
// 计算编队中心(使用第一个代理的位置或指定目标)
Vector3 formationCenter = Vector3.zero;
if (activeAgents.Count > 0 && activeAgents[0] != null)
{
formationCenter = activeAgents[0].transform.position;
}
// 获取相对位置
int positionIndex = agentIndex % formation.relativePositions.Length;
Vector3 relativePosition = formation.relativePositions[positionIndex];
// 应用编队旋转
Quaternion formationRotation = CalculateFormationRotation();
Vector3 worldPosition = formationCenter + formationRotation * relativePosition;
return worldPosition;
}
private Quaternion CalculateFormationRotation()
{
// 基于第一个代理的朝向或群体平均朝向计算编队旋转
if (activeAgents.Count > 0 && activeAgents[0] != null)
{
return activeAgents[0].transform.rotation;
}
return Quaternion.identity;
}
private void ApplyIndividualBehavior(IntelligentAgentController agent)
{
// 个体行为逻辑
// 可以在这里实现避免碰撞、保持距离等行为
}
public void RegisterAgent(IntelligentAgentController agent)
{
if (!activeAgents.Contains(agent) && activeAgents.Count < maxAgents)
{
activeAgents.Add(agent);
// 禁用自动漫游,由群体管理器控制
if (agent.autoConfigure)
{
agent.autoConfigure = false;
}
}
}
public void UnregisterAgent(IntelligentAgentController agent)
{
if (activeAgents.Contains(agent))
{
activeAgents.Remove(agent);
}
}
public void MoveFormationTo(Vector3 destination, int formationIndex = 0)
{
if (formationIndex < 0 || formationIndex >= formations.Count)
{
Debug.LogWarning($"编队索引 {formationIndex} 无效");
return;
}
// 将整个编队移动到目标位置
CrowdFormation formation = formations[formationIndex];
for (int i = 0; i < Mathf.Min(activeAgents.Count, formation.relativePositions.Length); i++)
{
IntelligentAgentController agent = activeAgents[i];
if (agent != null)
{
Vector3 relativePosition = formation.relativePositions[i];
Vector3 targetPosition = destination + relativePosition;
agent.SetDestination(targetPosition);
}
}
}
public void SetFormation(int formationIndex)
{
if (formationIndex >= 0 && formationIndex < formations.Count)
{
// 更新所有代理的编队位置
for (int i = 0; i < activeAgents.Count; i++)
{
if (i < formations[formationIndex].relativePositions.Length)
{
// 编队位置将在下一帧更新
}
}
}
}
private void OnDrawGizmos()
{
// 绘制群体信息
if (activeAgents != null && activeAgents.Count > 0)
{
Gizmos.color = Color.yellow;
// 绘制代理之间的连接线
for (int i = 0; i < activeAgents.Count - 1; i++)
{
if (activeAgents[i] != null && activeAgents[i + 1] != null)
{
Gizmos.DrawLine(
activeAgents[i].transform.position,
activeAgents[i + 1].transform.position
);
}
}
// 绘制群体边界
Bounds crowdBounds = CalculateCrowdBounds();
Gizmos.color = new Color(1, 1, 0, 0.3f);
Gizmos.DrawWireCube(crowdBounds.center, crowdBounds.size);
}
}
private Bounds CalculateCrowdBounds()
{
Bounds bounds = new Bounds();
bool hasBounds = false;
foreach (IntelligentAgentController agent in activeAgents)
{
if (agent != null)
{
if (!hasBounds)
{
bounds = new Bounds(agent.transform.position, Vector3.zero);
hasBounds = true;
}
else
{
bounds.Encapsulate(agent.transform.position);
}
}
}
return bounds;
}
}
5.4 运行时导航网格数据的生成与更新
动态环境是许多现代游戏的共同特征。运行时生成和更新导航网格数据对于支持可破坏环境、移动平台和玩家建造系统等功能至关重要。
5.4.1 动态导航网格生成系统
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
using System.Collections;
namespace DynamicNavigation
{
// 动态导航网格管理器
public class DynamicNavMeshManager : MonoBehaviour
{
[System.Serializable]
public class DynamicNavMeshSettings
{
public float updateThreshold = 1.0f; // 位置变化超过此值触发更新
public float minUpdateInterval = 0.5f; // 最小更新间隔
public bool incrementalUpdates = true; // 使用增量更新
public float incrementalUpdateRadius = 5.0f; // 增量更新半径
public int maxConcurrentUpdates = 2; // 最大并发更新数
}
[Header("动态设置")]
public DynamicNavMeshSettings settings;
[Header("调试")]
public bool showDebugVisualization = true;
public Color dynamicAreaColor = new Color(0, 1, 0, 0.3f);
private NavMeshData navMeshData;
private NavMeshDataInstance navMeshInstance;
private List<NavMeshBuildSource> staticSources;
private List<DynamicNavMeshObject> dynamicObjects;
private Dictionary<DynamicNavMeshObject, NavMeshBuildSource> dynamicSources;
private AsyncOperation currentUpdateOperation;
private Queue<Bounds> updateQueue;
private float lastUpdateTime;
private bool isUpdating;
private void Awake()
{
InitializeDynamicNavMeshSystem();
}
private void InitializeDynamicNavMeshSystem()
{
// 初始化数据结构
dynamicObjects = new List<DynamicNavMeshObject>();
dynamicSources = new Dictionary<DynamicNavMeshObject, NavMeshBuildSource>();
updateQueue = new Queue<Bounds>();
// 收集静态导航源
CollectStaticSources();
// 构建初始导航网格
BuildInitialNavMesh();
// 开始监控动态对象
StartCoroutine(MonitorDynamicObjects());
}
private void CollectStaticSources()
{
staticSources = new List<NavMeshBuildSource>();
// 收集所有标记为静态的导航源
NavMeshSourceTag[] allTags = FindObjectsOfType<NavMeshSourceTag>();
foreach (NavMeshSourceTag tag in allTags)
{
if (tag.gameObject.isStatic)
{
staticSources.AddRange(tag.Collect());
}
}
Debug.Log($"收集到 {staticSources.Count} 个静态导航源");
}
private void BuildInitialNavMesh()
{
// 合并静态和动态源
List<NavMeshBuildSource> allSources = new List<NavMeshBuildSource>(staticSources);
// 创建导航网格设置
NavMeshBuildSettings buildSettings = NavMesh.CreateSettings();
buildSettings.agentTypeID = 0;
// 计算边界
Bounds bounds = CalculateTotalBounds(allSources);
// 构建导航网格数据
navMeshData = NavMeshBuilder.BuildNavMeshData(
buildSettings,
allSources,
bounds,
Vector3.zero,
Quaternion.identity
);
// 添加到导航系统
navMeshInstance = NavMesh.AddNavMeshData(navMeshData);
Debug.Log("初始导航网格构建完成");
}
public void RegisterDynamicObject(DynamicNavMeshObject dynamicObject)
{
if (!dynamicObjects.Contains(dynamicObject))
{
dynamicObjects.Add(dynamicObject);
// 监听对象的变化
dynamicObject.OnTransformChanged += HandleDynamicObjectChanged;
dynamicObject.OnDestroyed += HandleDynamicObjectDestroyed;
// 初始添加对象到导航网格
AddDynamicObjectToNavMesh(dynamicObject);
}
}
public void UnregisterDynamicObject(DynamicNavMeshObject dynamicObject)
{
if (dynamicObjects.Contains(dynamicObject))
{
// 移除事件监听
dynamicObject.OnTransformChanged -= HandleDynamicObjectChanged;
dynamicObject.OnDestroyed -= HandleDynamicObjectDestroyed;
// 从导航网格中移除对象
RemoveDynamicObjectFromNavMesh(dynamicObject);
dynamicObjects.Remove(dynamicObject);
}
}
private void AddDynamicObjectToNavMesh(DynamicNavMeshObject dynamicObject)
{
// 收集对象的导航源
List<NavMeshBuildSource> objectSources = dynamicObject.CollectNavMeshSources();
if (objectSources.Count > 0)
{
// 存储源数据
foreach (NavMeshBuildSource source in objectSources)
{
dynamicSources[dynamicObject] = source;
}
// 触发导航网格更新
RequestNavMeshUpdateAroundObject(dynamicObject);
}
}
private void RemoveDynamicObjectFromNavMesh(DynamicNavMeshObject dynamicObject)
{
if (dynamicSources.ContainsKey(dynamicObject))
{
dynamicSources.Remove(dynamicObject);
// 触发导航网格更新
RequestNavMeshUpdateAroundObject(dynamicObject);
}
}
private void HandleDynamicObjectChanged(DynamicNavMeshObject changedObject, Vector3 positionDelta)
{
// 检查是否超过更新阈值
if (positionDelta.magnitude > settings.updateThreshold)
{
RequestNavMeshUpdateAroundObject(changedObject);
}
}
private void HandleDynamicObjectDestroyed(DynamicNavMeshObject destroyedObject)
{
UnregisterDynamicObject(destroyedObject);
}
private void RequestNavMeshUpdateAroundObject(DynamicNavMeshObject dynamicObject)
{
// 计算需要更新的区域边界
Bounds updateBounds = CalculateObjectBounds(dynamicObject);
updateBounds.Expand(settings.incrementalUpdateRadius);
// 添加到更新队列
updateQueue.Enqueue(updateBounds);
// 尝试开始更新
TryStartNavMeshUpdate();
}
private void RequestFullNavMeshUpdate()
{
// 请求完全更新
Bounds fullBounds = CalculateTotalBounds(GetAllSources());
updateQueue.Enqueue(fullBounds);
TryStartNavMeshUpdate();
}
private void TryStartNavMeshUpdate()
{
if (!isUpdating && updateQueue.Count > 0 &&
Time.time - lastUpdateTime > settings.minUpdateInterval)
{
StartCoroutine(ProcessNavMeshUpdate());
}
}
private IEnumerator ProcessNavMeshUpdate()
{
isUpdating = true;
while (updateQueue.Count > 0)
{
Bounds updateBounds = updateQueue.Dequeue();
// 检查是否应该使用增量更新
if (settings.incrementalUpdates && navMeshData != null)
{
yield return StartCoroutine(UpdateNavMeshIncrementally(updateBounds));
}
else
{
yield return StartCoroutine(RebuildFullNavMesh());
}
lastUpdateTime = Time.time;
// 限制并发更新数量
if (updateQueue.Count > 0 && settings.maxConcurrentUpdates > 1)
{
yield return new WaitForSeconds(0.1f);
}
}
isUpdating = false;
}
private IEnumerator UpdateNavMeshIncrementally(Bounds updateBounds)
{
// 收集更新区域内的所有源
List<NavMeshBuildSource> sourcesInBounds = CollectSourcesInBounds(updateBounds);
if (sourcesInBounds.Count == 0)
{
yield break;
}
// 创建增量更新设置
NavMeshBuildSettings buildSettings = NavMesh.CreateSettings();
buildSettings.agentTypeID = 0;
// 异步更新导航网格
AsyncOperation updateOperation = NavMeshBuilder.UpdateNavMeshDataAsync(
navMeshData,
buildSettings,
sourcesInBounds,
updateBounds
);
currentUpdateOperation = updateOperation;
// 等待更新完成
while (!updateOperation.isDone)
{
yield return null;
}
currentUpdateOperation = null;
Debug.Log($"增量导航网格更新完成,更新了 {sourcesInBounds.Count} 个源");
}
private IEnumerator RebuildFullNavMesh()
{
// 收集所有源
List<NavMeshBuildSource> allSources = GetAllSources();
if (allSources.Count == 0)
{
yield break;
}
// 创建新的导航网格数据
NavMeshBuildSettings buildSettings = NavMesh.CreateSettings();
buildSettings.agentTypeID = 0;
Bounds totalBounds = CalculateTotalBounds(allSources);
NavMeshData newNavMeshData = NavMeshBuilder.BuildNavMeshData(
buildSettings,
allSources,
totalBounds,
Vector3.zero,
Quaternion.identity
);
// 等待一帧以确保安全
yield return null;
// 替换旧的导航网格数据
if (navMeshInstance.valid)
{
NavMesh.RemoveNavMeshData(navMeshInstance);
}
navMeshData = newNavMeshData;
navMeshInstance = NavMesh.AddNavMeshData(navMeshData);
Debug.Log($"完全导航网格重建完成,包含 {allSources.Count} 个源");
}
private List<NavMeshBuildSource> GetAllSources()
{
List<NavMeshBuildSource> allSources = new List<NavMeshBuildSource>(staticSources);
// 添加所有动态源
foreach (NavMeshBuildSource source in dynamicSources.Values)
{
allSources.Add(source);
}
return allSources;
}
private List<NavMeshBuildSource> CollectSourcesInBounds(Bounds bounds)
{
List<NavMeshBuildSource> sourcesInBounds = new List<NavMeshBuildSource>();
// 添加边界内的静态源
foreach (NavMeshBuildSource source in staticSources)
{
if (bounds.Intersects(CalculateSourceBounds(source)))
{
sourcesInBounds.Add(source);
}
}
// 添加边界内的动态源
foreach (KeyValuePair<DynamicNavMeshObject, NavMeshBuildSource> entry in dynamicSources)
{
if (bounds.Intersects(CalculateSourceBounds(entry.Value)))
{
sourcesInBounds.Add(entry.Value);
}
}
return sourcesInBounds;
}
private Bounds CalculateObjectBounds(DynamicNavMeshObject dynamicObject)
{
// 计算动态对象的边界
Renderer renderer = dynamicObject.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds;
}
Collider collider = dynamicObject.GetComponent<Collider>();
if (collider != null)
{
return collider.bounds;
}
// 默认边界
return new Bounds(dynamicObject.transform.position, Vector3.one * 2f);
}
private Bounds CalculateSourceBounds(NavMeshBuildSource source)
{
// 简化版的边界计算
Vector3 position = source.transform.GetColumn(3);
return new Bounds(position, Vector3.one);
}
private Bounds CalculateTotalBounds(List<NavMeshBuildSource> sources)
{
Bounds totalBounds = new Bounds();
bool hasBounds = false;
foreach (NavMeshBuildSource source in sources)
{
Bounds sourceBounds = CalculateSourceBounds(source);
if (!hasBounds)
{
totalBounds = sourceBounds;
hasBounds = true;
}
else
{
totalBounds.Encapsulate(sourceBounds);
}
}
if (!hasBounds)
{
totalBounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
}
return totalBounds;
}
private IEnumerator MonitorDynamicObjects()
{
while (true)
{
yield return new WaitForSeconds(1.0f);
// 定期检查动态对象的状态
CheckDynamicObjectsValidity();
}
}
private void CheckDynamicObjectsValidity()
{
// 移除无效的动态对象
List<DynamicNavMeshObject> invalidObjects = new List<DynamicNavMeshObject>();
foreach (DynamicNavMeshObject dynamicObject in dynamicObjects)
{
if (dynamicObject == null || !dynamicObject.gameObject.activeInHierarchy)
{
invalidObjects.Add(dynamicObject);
}
}
foreach (DynamicNavMeshObject invalidObject in invalidObjects)
{
UnregisterDynamicObject(invalidObject);
}
}
private void OnDestroy()
{
// 清理导航网格数据
if (navMeshInstance.valid)
{
NavMesh.RemoveNavMeshData(navMeshInstance);
}
// 移除所有事件监听
foreach (DynamicNavMeshObject dynamicObject in dynamicObjects)
{
if (dynamicObject != null)
{
dynamicObject.OnTransformChanged -= HandleDynamicObjectChanged;
dynamicObject.OnDestroyed -= HandleDynamicObjectDestroyed;
}
}
}
private void OnDrawGizmos()
{
if (!showDebugVisualization)
{
return;
}
// 绘制动态对象区域
Gizmos.color = dynamicAreaColor;
foreach (DynamicNavMeshObject dynamicObject in dynamicObjects)
{
if (dynamicObject != null)
{
Bounds bounds = CalculateObjectBounds(dynamicObject);
bounds.Expand(settings.incrementalUpdateRadius);
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
}
// 绘制更新队列
if (updateQueue != null && updateQueue.Count > 0)
{
Gizmos.color = Color.red;
foreach (Bounds bounds in updateQueue)
{
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
}
}
}
// 动态导航网格对象接口
public interface IDynamicNavMeshObject
{
event System.Action<DynamicNavMeshObject, Vector3> OnTransformChanged;
event System.Action<DynamicNavMeshObject> OnDestroyed;
List<NavMeshBuildSource> CollectNavMeshSources();
void NotifyTransformChanged(Vector3 positionDelta);
}
// 动态导航网格对象基类
public abstract class DynamicNavMeshObject : MonoBehaviour, IDynamicNavMeshObject
{
public event System.Action<DynamicNavMeshObject, Vector3> OnTransformChanged;
public event System.Action<DynamicNavMeshObject> OnDestroyed;
[Header("导航设置")]
public int areaType = 0;
public bool isWalkable = true;
public bool affectsNavigation = true;
protected Vector3 lastPosition;
protected Quaternion lastRotation;
protected Vector3 lastScale;
protected DynamicNavMeshManager navMeshManager;
protected virtual void Start()
{
// 缓存初始变换
lastPosition = transform.position;
lastRotation = transform.rotation;
lastScale = transform.localScale;
// 查找并注册到导航网格管理器
navMeshManager = FindObjectOfType<DynamicNavMeshManager>();
if (navMeshManager != null && affectsNavigation)
{
navMeshManager.RegisterDynamicObject(this);
}
}
protected virtual void Update()
{
if (!affectsNavigation)
{
return;
}
// 检查变换是否发生变化
Vector3 positionDelta = transform.position - lastPosition;
Quaternion rotationDelta = transform.rotation * Quaternion.Inverse(lastRotation);
Vector3 scaleDelta = transform.localScale - lastScale;
bool hasChanged = positionDelta.sqrMagnitude > 0.0001f ||
rotationDelta.eulerAngles.sqrMagnitude > 0.1f ||
scaleDelta.sqrMagnitude > 0.0001f;
if (hasChanged)
{
// 通知变换变化
NotifyTransformChanged(positionDelta);
// 更新缓存
lastPosition = transform.position;
lastRotation = transform.rotation;
lastScale = transform.localScale;
}
}
protected virtual void OnDestroy()
{
// 通知对象被销毁
OnDestroyed?.Invoke(this);
}
public abstract List<NavMeshBuildSource> CollectNavMeshSources();
public void NotifyTransformChanged(Vector3 positionDelta)
{
OnTransformChanged?.Invoke(this, positionDelta);
}
}
// 动态网格对象实现
public class DynamicMeshObject : DynamicNavMeshObject
{
[Header("网格设置")]
public Mesh sourceMesh;
public NavMeshBuildSourceShape shape = NavMeshBuildSourceShape.Mesh;
public override List<NavMeshBuildSource> CollectNavMeshSources()
{
List<NavMeshBuildSource> sources = new List<NavMeshBuildSource>();
if (!affectsNavigation || sourceMesh == null)
{
return sources;
}
NavMeshBuildSource source = new NavMeshBuildSource
{
shape = shape,
sourceObject = sourceMesh,
transform = transform.localToWorldMatrix,
area = areaType
};
sources.Add(source);
return sources;
}
protected override void OnDrawGizmosSelected()
{
if (!showDebugVisualization)
{
return;
}
// 绘制动态对象的边界
Gizmos.color = isWalkable ? Color.green : Color.yellow;
Gizmos.matrix = transform.localToWorldMatrix;
if (sourceMesh != null)
{
Gizmos.DrawWireMesh(sourceMesh);
}
else
{
// 绘制默认立方体
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
}
}
}
// 动态障碍物对象
public class DynamicObstacle : DynamicNavMeshObject
{
[Header("障碍物设置")]
public ObstacleShape obstacleShape = ObstacleShape.Box;
public Vector3 obstacleSize = Vector3.one;
public bool isCarving = true; // 是否从导航网格中挖空
public enum ObstacleShape
{
Box,
Sphere,
Capsule
}
private NavMeshObstacle navMeshObstacle;
protected override void Start()
{
base.Start();
// 添加NavMeshObstacle组件
if (isCarving)
{
navMeshObstacle = gameObject.AddComponent<NavMeshObstacle>();
ConfigureNavMeshObstacle();
}
}
private void ConfigureNavMeshObstacle()
{
if (navMeshObstacle == null)
{
return;
}
// 配置障碍物形状
switch (obstacleShape)
{
case ObstacleShape.Box:
navMeshObstacle.shape = NavMeshObstacleShape.Box;
navMeshObstacle.size = obstacleSize;
break;
case ObstacleShape.Sphere:
navMeshObstacle.shape = NavMeshObstacleShape.Sphere;
navMeshObstacle.radius = obstacleSize.x;
break;
case ObstacleShape.Capsule:
navMeshObstacle.shape = NavMeshObstacleShape.Capsule;
navMeshObstacle.radius = obstacleSize.x;
navMeshObstacle.height = obstacleSize.y;
break;
}
navMeshObstacle.carving = isCarving;
navMeshObstacle.carveOnlyStationary = false;
}
public override List<NavMeshBuildSource> CollectNavMeshSources()
{
List<NavMeshBuildSource> sources = new List<NavMeshBuildSource>();
if (!affectsNavigation || isCarving)
{
// 如果使用NavMeshObstacle挖空,则不作为构建源
return sources;
}
// 作为不可行走区域添加到导航网格
NavMeshBuildSource source = new NavMeshBuildSource();
source.area = 1; // 不可行走区域
switch (obstacleShape)
{
case ObstacleShape.Box:
source.shape = NavMeshBuildSourceShape.Box;
source.size = obstacleSize;
break;
case ObstacleShape.Sphere:
source.shape = NavMeshBuildSourceShape.Sphere;
source.size = Vector3.one * obstacleSize.x;
break;
case ObstacleShape.Capsule:
source.shape = NavMeshBuildSourceShape.Capsule;
source.size = new Vector3(obstacleSize.x, obstacleSize.y, 0);
break;
}
source.transform = transform.localToWorldMatrix;
sources.Add(source);
return sources;
}
protected override void Update()
{
base.Update();
// 更新NavMeshObstacle位置
if (navMeshObstacle != null)
{
navMeshObstacle.transform.position = transform.position;
navMeshObstacle.transform.rotation = transform.rotation;
}
}
public void SetCarving(bool carving)
{
isCarving = carving;
if (navMeshObstacle != null)
{
navMeshObstacle.carving = carving;
}
}
protected override void OnDrawGizmosSelected()
{
if (!showDebugVisualization)
{
return;
}
// 绘制障碍物
Gizmos.color = isCarving ? Color.red : new Color(1, 0.5f, 0, 1);
Gizmos.matrix = transform.localToWorldMatrix;
switch (obstacleShape)
{
case ObstacleShape.Box:
Gizmos.DrawWireCube(Vector3.zero, obstacleSize);
break;
case ObstacleShape.Sphere:
Gizmos.DrawWireSphere(Vector3.zero, obstacleSize.x);
break;
case ObstacleShape.Capsule:
DrawWireCapsule(Vector3.zero, obstacleSize.x, obstacleSize.y);
break;
}
}
private void DrawWireCapsule(Vector3 center, float radius, float height)
{
// 绘制胶囊体线框
Vector3 up = Vector3.up * (height - radius * 2) * 0.5f;
// 绘制半球
for (int i = 0; i < 180; i += 30)
{
float angle1 = i * Mathf.Deg2Rad;
float angle2 = (i + 30) * Mathf.Deg2Rad;
Vector3 point1 = new Vector3(Mathf.Cos(angle1) * radius, up.y, Mathf.Sin(angle1) * radius);
Vector3 point2 = new Vector3(Mathf.Cos(angle2) * radius, up.y, Mathf.Sin(angle2) * radius);
Gizmos.DrawLine(center + up + point1, center + up + point2);
Gizmos.DrawLine(center - up + point1, center - up + point2);
}
// 绘制侧面线
for (int i = 0; i < 360; i += 45)
{
float angle = i * Mathf.Deg2Rad;
Vector3 point = new Vector3(Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius);
Gizmos.DrawLine(center + up + point, center - up + point);
}
}
}
}
5.4.2 性能优化与内存管理
动态导航网格更新可能对性能产生显著影响。以下是优化策略的实现:
// 动态导航网格性能优化器
public class DynamicNavMeshOptimizer : MonoBehaviour
{
[System.Serializable]
public class OptimizationSettings
{
public bool useLOD = true;
public float[] lodDistances = { 10f, 20f, 50f };
public float[] lodUpdateIntervals = { 0.5f, 1.0f, 2.0f };
public bool batchUpdates = true;
public int maxBatchSize = 5;
public float batchDelay = 0.1f;
public bool prioritizePlayerArea = true;
public float playerAreaRadius = 15f;
public float playerAreaUpdateInterval = 0.2f;
public bool useObjectPooling = true;
public int navMeshDataPoolSize = 10;
}
public OptimizationSettings settings;
private DynamicNavMeshManager navMeshManager;
private Transform playerTransform;
private Dictionary<DynamicNavMeshObject, int> objectLODLevels;
private Queue<Bounds> updateBatch;
private Coroutine batchUpdateCoroutine;
private class NavMeshDataPool
{
private Queue<NavMeshData> pool;
private int maxSize;
public NavMeshDataPool(int maxSize)
{
this.maxSize = maxSize;
pool = new Queue<NavMeshData>();
}
public NavMeshData Get()
{
if (pool.Count > 0)
{
return pool.Dequeue();
}
return null;
}
public void Return(NavMeshData data)
{
if (pool.Count < maxSize && data != null)
{
pool.Enqueue(data);
}
}
public void Clear()
{
foreach (NavMeshData data in pool)
{
// 清理导航网格数据
if (data != null)
{
// 注意:NavMeshData没有明确的Dispose方法
// 实际项目中可能需要自定义清理逻辑
}
}
pool.Clear();
}
}
private NavMeshDataPool navMeshDataPool;
private void Start()
{
navMeshManager = FindObjectOfType<DynamicNavMeshManager>();
playerTransform = FindPlayerTransform();
objectLODLevels = new Dictionary<DynamicNavMeshObject, int>();
updateBatch = new Queue<Bounds>();
if (settings.useObjectPooling)
{
navMeshDataPool = new NavMeshDataPool(settings.navMeshDataPoolSize);
}
// 开始LOD更新协程
if (settings.useLOD)
{
StartCoroutine(UpdateLODLevels());
}
// 开始玩家区域优先更新协程
if (settings.prioritizePlayerArea)
{
StartCoroutine(UpdatePlayerArea());
}
}
private Transform FindPlayerTransform()
{
// 查找玩家对象(根据项目结构调整)
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
return player.transform;
}
// 如果没有找到,使用主相机的位置
if (Camera.main != null)
{
return Camera.main.transform;
}
return null;
}
private IEnumerator UpdateLODLevels()
{
while (true)
{
yield return new WaitForSeconds(1.0f);
UpdateAllObjectLODLevels();
}
}
private void UpdateAllObjectLODLevels()
{
if (playerTransform == null || !settings.useLOD)
{
return;
}
// 更新所有动态对象的LOD级别
// 注意:这里需要访问DynamicNavMeshManager的动态对象列表
// 实际实现中可能需要修改DynamicNavMeshManager以提供访问接口
}
private int CalculateLODLevelForObject(Vector3 objectPosition)
{
if (playerTransform == null)
{
return 0;
}
float distance = Vector3.Distance(objectPosition, playerTransform.position);
for (int i = 0; i < settings.lodDistances.Length; i++)
{
if (distance <= settings.lodDistances[i])
{
return i;
}
}
return settings.lodDistances.Length;
}
private IEnumerator UpdatePlayerArea()
{
while (true)
{
yield return new WaitForSeconds(settings.playerAreaUpdateInterval);
if (playerTransform != null && settings.prioritizePlayerArea)
{
UpdateNavMeshAroundPlayer();
}
}
}
private void UpdateNavMeshAroundPlayer()
{
if (playerTransform == null || navMeshManager == null)
{
return;
}
// 创建玩家周围的更新区域
Bounds playerBounds = new Bounds(
playerTransform.position,
Vector3.one * settings.playerAreaRadius * 2
);
// 请求更新
RequestNavMeshUpdate(playerBounds, true);
}
public void RequestNavMeshUpdate(Bounds bounds, bool isPriority = false)
{
if (navMeshManager == null)
{
return;
}
if (settings.batchUpdates && !isPriority)
{
// 添加到批处理队列
updateBatch.Enqueue(bounds);
if (batchUpdateCoroutine == null)
{
batchUpdateCoroutine = StartCoroutine(ProcessBatchUpdates());
}
}
else
{
// 立即处理优先级更新
// 这里需要调用DynamicNavMeshManager的更新方法
// navMeshManager.RequestUpdateInBounds(bounds);
}
}
private IEnumerator ProcessBatchUpdates()
{
while (updateBatch.Count > 0)
{
// 收集一批更新
List<Bounds> batch = new List<Bounds>();
for (int i = 0; i < settings.maxBatchSize && updateBatch.Count > 0; i++)
{
batch.Add(updateBatch.Dequeue());
}
// 合并边界
Bounds mergedBounds = MergeBounds(batch);
// 执行更新
// navMeshManager.RequestUpdateInBounds(mergedBounds);
// 等待批处理延迟
yield return new WaitForSeconds(settings.batchDelay);
}
batchUpdateCoroutine = null;
}
private Bounds MergeBounds(List<Bounds> boundsList)
{
if (boundsList.Count == 0)
{
return new Bounds(Vector3.zero, Vector3.zero);
}
Bounds merged = boundsList[0];
for (int i = 1; i < boundsList.Count; i++)
{
merged.Encapsulate(boundsList[i]);
}
return merged;
}
public NavMeshData GetNavMeshDataFromPool()
{
if (settings.useObjectPooling && navMeshDataPool != null)
{
return navMeshDataPool.Get();
}
return null;
}
public void ReturnNavMeshDataToPool(NavMeshData data)
{
if (settings.useObjectPooling && navMeshDataPool != null)
{
navMeshDataPool.Return(data);
}
}
private void OnDestroy()
{
// 清理对象池
if (navMeshDataPool != null)
{
navMeshDataPool.Clear();
}
// 停止所有协程
if (batchUpdateCoroutine != null)
{
StopCoroutine(batchUpdateCoroutine);
}
}
}
5.5 导航网格实例的生命周期控制
在复杂的游戏场景中,可能需要管理多个导航网格实例,每个实例对应不同的区域或代理类型。有效的生命周期管理确保内存使用高效且不会产生泄漏。
5.5.1 导航网格实例管理器
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
namespace NavMeshLifecycle
{
// 导航网格实例信息
[System.Serializable]
public class NavMeshInstanceInfo
{
public string instanceId;
public NavMeshData navMeshData;
public NavMeshDataInstance navMeshInstance;
public Bounds bounds;
public int agentTypeID;
public bool isActive;
public float lastUsedTime;
// 使用统计
public int queryCount;
public float averageQueryTime;
public NavMeshInstanceInfo(string id, NavMeshData data, Bounds bounds, int agentTypeID)
{
this.instanceId = id;
this.navMeshData = data;
this.bounds = bounds;
this.agentTypeID = agentTypeID;
this.isActive = false;
this.lastUsedTime = Time.time;
this.queryCount = 0;
this.averageQueryTime = 0f;
}
public void RecordQuery(float queryTime)
{
queryCount++;
averageQueryTime = (averageQueryTime * (queryCount - 1) + queryTime) / queryCount;
lastUsedTime = Time.time;
}
public float CalculatePriority(float currentTime)
{
// 优先级计算:基于使用频率和最近使用时间
float timeSinceLastUse = currentTime - lastUsedTime;
float usagePriority = Mathf.Clamp01(1.0f - timeSinceLastUse / 300f); // 5分钟衰减
float queryPriority = Mathf.Clamp01(queryCount / 100f);
return (usagePriority * 0.7f + queryPriority * 0.3f);
}
}
// 导航网格实例管理器
public class NavMeshInstanceManager : MonoBehaviour
{
[System.Serializable]
public class LifecycleSettings
{
public int maxInstances = 10;
public float unusedInstanceTimeout = 300f; // 5分钟
public bool autoRemoveUnused = true;
public float cleanupInterval = 60f; // 每60秒清理一次
public bool logLifecycleEvents = true;
}
[Header("生命周期设置")]
public LifecycleSettings settings;
[Header("性能优化")]
public bool useBackgroundLoading = true;
public float backgroundLoadBudget = 0.01f; // 每帧最多花费10ms
private Dictionary<string, NavMeshInstanceInfo> instances;
private Queue<NavMeshBuildRequest> buildQueue;
private float lastCleanupTime;
private class NavMeshBuildRequest
{
public string instanceId;
public List<NavMeshBuildSource> sources;
public Bounds bounds;
public int agentTypeID;
public System.Action<NavMeshInstanceInfo> callback;
}
private void Awake()
{
InitializeManager();
}
private void InitializeManager()
{
instances = new Dictionary<string, NavMeshInstanceInfo>();
buildQueue = new Queue<NavMeshBuildRequest>();
lastCleanupTime = Time.time;
// 开始后台处理协程
if (useBackgroundLoading)
{
StartCoroutine(ProcessBackgroundBuilds());
}
// 开始清理协程
if (settings.autoRemoveUnused)
{
StartCoroutine(AutoCleanupRoutine());
}
}
public string CreateNavMeshInstance(
List<NavMeshBuildSource> sources,
Bounds bounds,
int agentTypeID,
bool buildImmediately = true)
{
// 生成唯一ID
string instanceId = GenerateInstanceId(bounds, agentTypeID);
// 检查是否已存在相同实例
if (instances.ContainsKey(instanceId))
{
NavMeshInstanceInfo existing = instances[instanceId];
existing.lastUsedTime = Time.time;
if (settings.logLifecycleEvents)
{
Debug.Log($"重用现有导航网格实例: {instanceId}");
}
return instanceId;
}
// 检查实例数量限制
if (instances.Count >= settings.maxInstances)
{
RemoveLowestPriorityInstance();
}
if (buildImmediately)
{
// 立即构建
NavMeshInstanceInfo instanceInfo = BuildNavMeshInstance(
instanceId,
sources,
bounds,
agentTypeID
);
instances[instanceId] = instanceInfo;
return instanceId;
}
else
{
// 加入构建队列
NavMeshBuildRequest request = new NavMeshBuildRequest
{
instanceId = instanceId,
sources = sources,
bounds = bounds,
agentTypeID = agentTypeID
};
buildQueue.Enqueue(request);
return instanceId;
}
}
public void CreateNavMeshInstanceAsync(
List<NavMeshBuildSource> sources,
Bounds bounds,
int agentTypeID,
System.Action<NavMeshInstanceInfo> callback)
{
string instanceId = GenerateInstanceId(bounds, agentTypeID);
if (instances.ContainsKey(instanceId))
{
callback?.Invoke(instances[instanceId]);
return;
}
NavMeshBuildRequest request = new NavMeshBuildRequest
{
instanceId = instanceId,
sources = sources,
bounds = bounds,
agentTypeID = agentTypeID,
callback = callback
};
buildQueue.Enqueue(request);
}
private NavMeshInstanceInfo BuildNavMeshInstance(
string instanceId,
List<NavMeshBuildSource> sources,
Bounds bounds,
int agentTypeID)
{
System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
// 创建导航网格设置
NavMeshBuildSettings buildSettings = NavMesh.CreateSettings();
buildSettings.agentTypeID = agentTypeID;
// 构建导航网格数据
NavMeshData navMeshData = NavMeshBuilder.BuildNavMeshData(
buildSettings,
sources,
bounds,
Vector3.zero,
Quaternion.identity
);
// 创建导航网格实例
NavMeshDataInstance navMeshInstance = NavMesh.AddNavMeshData(navMeshData);
stopwatch.Stop();
if (settings.logLifecycleEvents)
{
Debug.Log($"构建导航网格实例 {instanceId} 完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
}
// 创建实例信息
NavMeshInstanceInfo instanceInfo = new NavMeshInstanceInfo(
instanceId,
navMeshData,
bounds,
agentTypeID
);
instanceInfo.navMeshInstance = navMeshInstance;
instanceInfo.isActive = true;
return instanceInfo;
}
private System.Collections.IEnumerator ProcessBackgroundBuilds()
{
while (true)
{
if (buildQueue.Count > 0)
{
System.Diagnostics.Stopwatch frameTimer = System.Diagnostics.Stopwatch.StartNew();
while (buildQueue.Count > 0 &&
frameTimer.Elapsed.TotalMilliseconds < backgroundLoadBudget)
{
NavMeshBuildRequest request = buildQueue.Dequeue();
// 检查实例数量限制
if (instances.Count >= settings.maxInstances)
{
RemoveLowestPriorityInstance();
}
// 构建实例
NavMeshInstanceInfo instanceInfo = BuildNavMeshInstance(
request.instanceId,
request.sources,
request.bounds,
request.agentTypeID
);
instances[request.instanceId] = instanceInfo;
// 调用回调
request.callback?.Invoke(instanceInfo);
}
}
yield return null;
}
}
private string GenerateInstanceId(Bounds bounds, int agentTypeID)
{
// 基于边界和代理类型生成唯一ID
Vector3 center = bounds.center;
Vector3 size = bounds.size;
return $"NavMesh_{agentTypeID}_{center.x:F1}_{center.y:F1}_{center.z:F1}_{size.x:F1}_{size.y:F1}_{size.z:F1}";
}
public bool ActivateInstance(string instanceId)
{
if (instances.ContainsKey(instanceId))
{
NavMeshInstanceInfo instance = instances[instanceId];
if (!instance.isActive)
{
instance.navMeshInstance = NavMesh.AddNavMeshData(instance.navMeshData);
instance.isActive = true;
if (settings.logLifecycleEvents)
{
Debug.Log($"激活导航网格实例: {instanceId}");
}
}
instance.lastUsedTime = Time.time;
return true;
}
return false;
}
public bool DeactivateInstance(string instanceId)
{
if (instances.ContainsKey(instanceId))
{
NavMeshInstanceInfo instance = instances[instanceId];
if (instance.isActive)
{
NavMesh.RemoveNavMeshData(instance.navMeshInstance);
instance.isActive = false;
if (settings.logLifecycleEvents)
{
Debug.Log($"停用导航网格实例: {instanceId}");
}
}
return true;
}
return false;
}
public void RemoveInstance(string instanceId)
{
if (instances.ContainsKey(instanceId))
{
NavMeshInstanceInfo instance = instances[instanceId];
if (instance.isActive)
{
NavMesh.RemoveNavMeshData(instance.navMeshInstance);
}
instances.Remove(instanceId);
if (settings.logLifecycleEvents)
{
Debug.Log($"移除导航网格实例: {instanceId}");
}
}
}
private void RemoveLowestPriorityInstance()
{
if (instances.Count == 0)
{
return;
}
string lowestPriorityId = null;
float lowestPriority = float.MaxValue;
float currentTime = Time.time;
foreach (KeyValuePair<string, NavMeshInstanceInfo> entry in instances)
{
float priority = entry.Value.CalculatePriority(currentTime);
if (priority < lowestPriority)
{
lowestPriority = priority;
lowestPriorityId = entry.Key;
}
}
if (lowestPriorityId != null)
{
RemoveInstance(lowestPriorityId);
}
}
private System.Collections.IEnumerator AutoCleanupRoutine()
{
while (true)
{
yield return new WaitForSeconds(settings.cleanupInterval);
CleanupUnusedInstances();
}
}
private void CleanupUnusedInstances()
{
float currentTime = Time.time;
List<string> instancesToRemove = new List<string>();
foreach (KeyValuePair<string, NavMeshInstanceInfo> entry in instances)
{
float timeSinceLastUse = currentTime - entry.Value.lastUsedTime;
if (timeSinceLastUse > settings.unusedInstanceTimeout)
{
instancesToRemove.Add(entry.Key);
}
}
foreach (string instanceId in instancesToRemove)
{
RemoveInstance(instanceId);
}
if (settings.logLifecycleEvents && instancesToRemove.Count > 0)
{
Debug.Log($"清理了 {instancesToRemove.Count} 个未使用的导航网格实例");
}
}
public NavMeshInstanceInfo GetInstanceInfo(string instanceId)
{
if (instances.ContainsKey(instanceId))
{
return instances[instanceId];
}
return null;
}
public List<string> GetAllInstanceIds()
{
return new List<string>(instances.Keys);
}
public void RecordQuery(string instanceId, float queryTime)
{
if (instances.ContainsKey(instanceId))
{
instances[instanceId].RecordQuery(queryTime);
}
}
private void OnDestroy()
{
// 清理所有实例
List<string> allInstanceIds = new List<string>(instances.Keys);
foreach (string instanceId in allInstanceIds)
{
RemoveInstance(instanceId);
}
instances.Clear();
buildQueue.Clear();
}
public void DrawDebugVisualization()
{
foreach (NavMeshInstanceInfo instance in instances.Values)
{
if (instance.isActive)
{
// 绘制活动实例的边界
Gizmos.color = Color.green;
}
else
{
// 绘制非活动实例的边界
Gizmos.color = Color.gray;
}
Gizmos.DrawWireCube(instance.bounds.center, instance.bounds.size);
// 绘制实例信息
Vector3 labelPos = instance.bounds.center + Vector3.up * instance.bounds.extents.y;
#if UNITY_EDITOR
UnityEditor.Handles.Label(labelPos,
$"{instance.instanceId}\nQueries: {instance.queryCount}\nAvg Time: {instance.averageQueryTime:F2}ms");
#endif
}
}
}
// 导航网格查询代理
public class NavMeshQueryAgent : MonoBehaviour
{
[Header("查询设置")]
public string navMeshInstanceId;
public int agentTypeID = 0;
public float queryTimeout = 5.0f;
private NavMeshInstanceManager instanceManager;
private NavMeshInstanceInfo currentInstance;
private System.Diagnostics.Stopwatch queryTimer;
private void Start()
{
instanceManager = FindObjectOfType<NavMeshInstanceManager>();
queryTimer = new System.Diagnostics.Stopwatch();
if (instanceManager == null)
{
Debug.LogError("未找到 NavMeshInstanceManager");
return;
}
// 获取或创建导航网格实例
if (string.IsNullOrEmpty(navMeshInstanceId))
{
// 需要根据当前位置创建或查找实例
// 这里简化处理
}
else
{
currentInstance = instanceManager.GetInstanceInfo(navMeshInstanceId);
if (currentInstance == null)
{
Debug.LogWarning($"导航网格实例 {navMeshInstanceId} 不存在");
}
}
}
public NavMeshPath QueryPath(Vector3 start, Vector3 end)
{
if (currentInstance == null || !currentInstance.isActive)
{
Debug.LogWarning("导航网格实例不可用");
return null;
}
queryTimer.Restart();
// 执行路径查询
NavMeshPath path = new NavMeshPath();
bool success = NavMesh.CalculatePath(start, end, NavMesh.AllAreas, path);
queryTimer.Stop();
// 记录查询性能
instanceManager.RecordQuery(navMeshInstanceId, (float)queryTimer.Elapsed.TotalMilliseconds);
if (!success)
{
Debug.LogWarning("路径查询失败");
return null;
}
return path;
}
public NavMeshHit QueryRaycast(Vector3 start, Vector3 direction, float maxDistance)
{
if (currentInstance == null || !currentInstance.isActive)
{
Debug.LogWarning("导航网格实例不可用");
return new NavMeshHit();
}
queryTimer.Restart();
NavMeshHit hit;
bool success = NavMesh.Raycast(start, start + direction * maxDistance, out hit, NavMesh.AllAreas);
queryTimer.Stop();
instanceManager.RecordQuery(navMeshInstanceId, (float)queryTimer.Elapsed.TotalMilliseconds);
if (!success)
{
Debug.LogWarning("射线查询失败");
}
return hit;
}
public NavMeshHit QuerySamplePosition(Vector3 position, float maxDistance)
{
if (currentInstance == null || !currentInstance.isActive)
{
Debug.LogWarning("导航网格实例不可用");
return new NavMeshHit();
}
queryTimer.Restart();
NavMeshHit hit;
bool success = NavMesh.SamplePosition(position, out hit, maxDistance, NavMesh.AllAreas);
queryTimer.Stop();
instanceManager.RecordQuery(navMeshInstanceId, (float)queryTimer.Elapsed.TotalMilliseconds);
if (!success)
{
Debug.LogWarning("位置采样失败");
}
return hit;
}
public void SwitchNavMeshInstance(string newInstanceId)
{
if (instanceManager == null)
{
return;
}
// 停用当前实例
if (currentInstance != null && currentInstance.isActive)
{
instanceManager.DeactivateInstance(navMeshInstanceId);
}
// 激活新实例
if (instanceManager.ActivateInstance(newInstanceId))
{
navMeshInstanceId = newInstanceId;
currentInstance = instanceManager.GetInstanceInfo(newInstanceId);
Debug.Log($"切换到导航网格实例: {newInstanceId}");
}
else
{
Debug.LogWarning($"无法切换到导航网格实例: {newInstanceId}");
}
}
}
}
总结
Unity的导航网格系统提供了强大而灵活的工具集,能够满足从简单寻路到复杂动态环境导航的各种需求。通过合理使用多代理类型支持、动态更新机制和实例生命周期管理,开发者可以创建出既智能又高效的AI导航系统。
关键要点总结:
-
多代理类型支持:为不同类型的AI角色创建定制化的导航参数,确保各种角色都能在场景中自然移动。
-
动态导航网格更新:通过增量更新和智能批处理,实现对动态环境的实时响应,同时保持高性能。
-
生命周期管理:有效管理导航网格实例的创建、激活、停用和销毁,避免内存泄漏和性能问题。
-
性能优化:使用LOD系统、对象池和异步处理等技术,确保导航系统在各种硬件上都能流畅运行。
-
调试与可视化:开发强大的调试工具,帮助识别和解决导航问题,提高开发效率。
在实际商业项目中,建议根据具体需求选择合适的导航策略。对于开放世界游戏,可能需要复杂的动态更新和LOD系统;对于竞技场战斗游戏,可能更注重多代理避障和群体行为;而对于策略游戏,则可能需要高效的路径查询和批量处理。
通过深入理解和灵活应用Unity的导航网格API,开发者可以创建出能够处理复杂导航场景的AI系统,为玩家提供更加真实和沉浸式的游戏体验。
更多推荐

所有评论(0)