NavMeshSurface.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. using System.Collections.Generic;
  2. #if UNITY_EDITOR
  3. using UnityEditor;
  4. using UnityEditor.SceneManagement;
  5. #endif
  6. namespace UnityEngine.AI
  7. {
  8. public enum CollectObjects
  9. {
  10. All = 0,
  11. Volume = 1,
  12. Children = 2,
  13. }
  14. [ExecuteAlways]
  15. [DefaultExecutionOrder(-102)]
  16. [AddComponentMenu("Navigation/NavMeshSurface", 30)]
  17. [HelpURL("https://github.com/Unity-Technologies/NavMeshComponents#documentation-draft")]
  18. public class NavMeshSurface : MonoBehaviour
  19. {
  20. [SerializeField]
  21. int m_AgentTypeID;
  22. public int agentTypeID { get { return m_AgentTypeID; } set { m_AgentTypeID = value; } }
  23. [SerializeField]
  24. CollectObjects m_CollectObjects = CollectObjects.All;
  25. public CollectObjects collectObjects { get { return m_CollectObjects; } set { m_CollectObjects = value; } }
  26. [SerializeField]
  27. Vector3 m_Size = new Vector3(10.0f, 10.0f, 10.0f);
  28. public Vector3 size { get { return m_Size; } set { m_Size = value; } }
  29. [SerializeField]
  30. Vector3 m_Center = new Vector3(0, 2.0f, 0);
  31. public Vector3 center { get { return m_Center; } set { m_Center = value; } }
  32. [SerializeField]
  33. LayerMask m_LayerMask = ~0;
  34. public LayerMask layerMask { get { return m_LayerMask; } set { m_LayerMask = value; } }
  35. [SerializeField]
  36. NavMeshCollectGeometry m_UseGeometry = NavMeshCollectGeometry.RenderMeshes;
  37. public NavMeshCollectGeometry useGeometry { get { return m_UseGeometry; } set { m_UseGeometry = value; } }
  38. [SerializeField]
  39. int m_DefaultArea;
  40. public int defaultArea { get { return m_DefaultArea; } set { m_DefaultArea = value; } }
  41. [SerializeField]
  42. bool m_IgnoreNavMeshAgent = true;
  43. public bool ignoreNavMeshAgent { get { return m_IgnoreNavMeshAgent; } set { m_IgnoreNavMeshAgent = value; } }
  44. [SerializeField]
  45. bool m_IgnoreNavMeshObstacle = true;
  46. public bool ignoreNavMeshObstacle { get { return m_IgnoreNavMeshObstacle; } set { m_IgnoreNavMeshObstacle = value; } }
  47. [SerializeField]
  48. bool m_OverrideTileSize;
  49. public bool overrideTileSize { get { return m_OverrideTileSize; } set { m_OverrideTileSize = value; } }
  50. [SerializeField]
  51. int m_TileSize = 256;
  52. public int tileSize { get { return m_TileSize; } set { m_TileSize = value; } }
  53. [SerializeField]
  54. bool m_OverrideVoxelSize;
  55. public bool overrideVoxelSize { get { return m_OverrideVoxelSize; } set { m_OverrideVoxelSize = value; } }
  56. [SerializeField]
  57. float m_VoxelSize;
  58. public float voxelSize { get { return m_VoxelSize; } set { m_VoxelSize = value; } }
  59. // Currently not supported advanced options
  60. [SerializeField]
  61. bool m_BuildHeightMesh;
  62. public bool buildHeightMesh { get { return m_BuildHeightMesh; } set { m_BuildHeightMesh = value; } }
  63. // Reference to whole scene navmesh data asset.
  64. [UnityEngine.Serialization.FormerlySerializedAs("m_BakedNavMeshData")]
  65. [SerializeField]
  66. NavMeshData m_NavMeshData;
  67. public NavMeshData navMeshData { get { return m_NavMeshData; } set { m_NavMeshData = value; } }
  68. // Do not serialize - runtime only state.
  69. NavMeshDataInstance m_NavMeshDataInstance;
  70. Vector3 m_LastPosition = Vector3.zero;
  71. Quaternion m_LastRotation = Quaternion.identity;
  72. static readonly List<NavMeshSurface> s_NavMeshSurfaces = new List<NavMeshSurface>();
  73. public static List<NavMeshSurface> activeSurfaces
  74. {
  75. get { return s_NavMeshSurfaces; }
  76. }
  77. void OnEnable()
  78. {
  79. Register(this);
  80. AddData();
  81. }
  82. void OnDisable()
  83. {
  84. RemoveData();
  85. Unregister(this);
  86. }
  87. public void AddData()
  88. {
  89. #if UNITY_EDITOR
  90. var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(this);
  91. var isPrefab = isInPreviewScene || EditorUtility.IsPersistent(this);
  92. if (isPrefab)
  93. {
  94. //Debug.LogFormat("NavMeshData from {0}.{1} will not be added to the NavMesh world because the gameObject is a prefab.",
  95. // gameObject.name, name);
  96. return;
  97. }
  98. #endif
  99. if (m_NavMeshDataInstance.valid)
  100. return;
  101. if (m_NavMeshData != null)
  102. {
  103. m_NavMeshDataInstance = NavMesh.AddNavMeshData(m_NavMeshData, transform.position, transform.rotation);
  104. m_NavMeshDataInstance.owner = this;
  105. }
  106. m_LastPosition = transform.position;
  107. m_LastRotation = transform.rotation;
  108. }
  109. public void RemoveData()
  110. {
  111. m_NavMeshDataInstance.Remove();
  112. m_NavMeshDataInstance = new NavMeshDataInstance();
  113. }
  114. public NavMeshBuildSettings GetBuildSettings()
  115. {
  116. var buildSettings = NavMesh.GetSettingsByID(m_AgentTypeID);
  117. if (buildSettings.agentTypeID == -1)
  118. {
  119. Debug.LogWarning("No build settings for agent type ID " + agentTypeID, this);
  120. buildSettings.agentTypeID = m_AgentTypeID;
  121. }
  122. if (overrideTileSize)
  123. {
  124. buildSettings.overrideTileSize = true;
  125. buildSettings.tileSize = tileSize;
  126. }
  127. if (overrideVoxelSize)
  128. {
  129. buildSettings.overrideVoxelSize = true;
  130. buildSettings.voxelSize = voxelSize;
  131. }
  132. return buildSettings;
  133. }
  134. public void BuildNavMesh()
  135. {
  136. var sources = CollectSources();
  137. // Use unscaled bounds - this differs in behaviour from e.g. collider components.
  138. // But is similar to reflection probe - and since navmesh data has no scaling support - it is the right choice here.
  139. var sourcesBounds = new Bounds(m_Center, Abs(m_Size));
  140. if (m_CollectObjects == CollectObjects.All || m_CollectObjects == CollectObjects.Children)
  141. {
  142. sourcesBounds = CalculateWorldBounds(sources);
  143. }
  144. var data = NavMeshBuilder.BuildNavMeshData(GetBuildSettings(),
  145. sources, sourcesBounds, transform.position, transform.rotation);
  146. if (data != null)
  147. {
  148. data.name = gameObject.name;
  149. RemoveData();
  150. m_NavMeshData = data;
  151. if (isActiveAndEnabled)
  152. AddData();
  153. }
  154. }
  155. public AsyncOperation UpdateNavMesh(NavMeshData data)
  156. {
  157. var sources = CollectSources();
  158. // Use unscaled bounds - this differs in behaviour from e.g. collider components.
  159. // But is similar to reflection probe - and since navmesh data has no scaling support - it is the right choice here.
  160. var sourcesBounds = new Bounds(m_Center, Abs(m_Size));
  161. if (m_CollectObjects == CollectObjects.All || m_CollectObjects == CollectObjects.Children)
  162. sourcesBounds = CalculateWorldBounds(sources);
  163. return NavMeshBuilder.UpdateNavMeshDataAsync(data, GetBuildSettings(), sources, sourcesBounds);
  164. }
  165. static void Register(NavMeshSurface surface)
  166. {
  167. #if UNITY_EDITOR
  168. var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(surface);
  169. var isPrefab = isInPreviewScene || EditorUtility.IsPersistent(surface);
  170. if (isPrefab)
  171. {
  172. //Debug.LogFormat("NavMeshData from {0}.{1} will not be added to the NavMesh world because the gameObject is a prefab.",
  173. // surface.gameObject.name, surface.name);
  174. return;
  175. }
  176. #endif
  177. if (s_NavMeshSurfaces.Count == 0)
  178. NavMesh.onPreUpdate += UpdateActive;
  179. if (!s_NavMeshSurfaces.Contains(surface))
  180. s_NavMeshSurfaces.Add(surface);
  181. }
  182. static void Unregister(NavMeshSurface surface)
  183. {
  184. s_NavMeshSurfaces.Remove(surface);
  185. if (s_NavMeshSurfaces.Count == 0)
  186. NavMesh.onPreUpdate -= UpdateActive;
  187. }
  188. static void UpdateActive()
  189. {
  190. for (var i = 0; i < s_NavMeshSurfaces.Count; ++i)
  191. s_NavMeshSurfaces[i].UpdateDataIfTransformChanged();
  192. }
  193. void AppendModifierVolumes(ref List<NavMeshBuildSource> sources)
  194. {
  195. #if UNITY_EDITOR
  196. var myStage = StageUtility.GetStageHandle(gameObject);
  197. if (!myStage.IsValid())
  198. return;
  199. #endif
  200. // Modifiers
  201. List<NavMeshModifierVolume> modifiers;
  202. if (m_CollectObjects == CollectObjects.Children)
  203. {
  204. modifiers = new List<NavMeshModifierVolume>(GetComponentsInChildren<NavMeshModifierVolume>());
  205. modifiers.RemoveAll(x => !x.isActiveAndEnabled);
  206. }
  207. else
  208. {
  209. modifiers = NavMeshModifierVolume.activeModifiers;
  210. }
  211. foreach (var m in modifiers)
  212. {
  213. if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
  214. continue;
  215. if (!m.AffectsAgentType(m_AgentTypeID))
  216. continue;
  217. #if UNITY_EDITOR
  218. if (!myStage.Contains(m.gameObject))
  219. continue;
  220. #endif
  221. var mcenter = m.transform.TransformPoint(m.center);
  222. var scale = m.transform.lossyScale;
  223. var msize = new Vector3(m.size.x * Mathf.Abs(scale.x), m.size.y * Mathf.Abs(scale.y), m.size.z * Mathf.Abs(scale.z));
  224. var src = new NavMeshBuildSource();
  225. src.shape = NavMeshBuildSourceShape.ModifierBox;
  226. src.transform = Matrix4x4.TRS(mcenter, m.transform.rotation, Vector3.one);
  227. src.size = msize;
  228. src.area = m.area;
  229. sources.Add(src);
  230. }
  231. }
  232. List<NavMeshBuildSource> CollectSources()
  233. {
  234. var sources = new List<NavMeshBuildSource>();
  235. var markups = new List<NavMeshBuildMarkup>();
  236. List<NavMeshModifier> modifiers;
  237. if (m_CollectObjects == CollectObjects.Children)
  238. {
  239. modifiers = new List<NavMeshModifier>(GetComponentsInChildren<NavMeshModifier>());
  240. modifiers.RemoveAll(x => !x.isActiveAndEnabled);
  241. }
  242. else
  243. {
  244. modifiers = NavMeshModifier.activeModifiers;
  245. }
  246. foreach (var m in modifiers)
  247. {
  248. if ((m_LayerMask & (1 << m.gameObject.layer)) == 0)
  249. continue;
  250. if (!m.AffectsAgentType(m_AgentTypeID))
  251. continue;
  252. var markup = new NavMeshBuildMarkup();
  253. markup.root = m.transform;
  254. markup.overrideArea = m.overrideArea;
  255. markup.area = m.area;
  256. markup.ignoreFromBuild = m.ignoreFromBuild;
  257. markups.Add(markup);
  258. }
  259. #if UNITY_EDITOR
  260. if (!EditorApplication.isPlaying)
  261. {
  262. if (m_CollectObjects == CollectObjects.All)
  263. {
  264. UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
  265. null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
  266. }
  267. else if (m_CollectObjects == CollectObjects.Children)
  268. {
  269. UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
  270. transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
  271. }
  272. else if (m_CollectObjects == CollectObjects.Volume)
  273. {
  274. Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
  275. var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
  276. UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage(
  277. worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, gameObject.scene, sources);
  278. }
  279. }
  280. else
  281. #endif
  282. {
  283. if (m_CollectObjects == CollectObjects.All)
  284. {
  285. NavMeshBuilder.CollectSources(null, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
  286. }
  287. else if (m_CollectObjects == CollectObjects.Children)
  288. {
  289. NavMeshBuilder.CollectSources(transform, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
  290. }
  291. else if (m_CollectObjects == CollectObjects.Volume)
  292. {
  293. Matrix4x4 localToWorld = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
  294. var worldBounds = GetWorldBounds(localToWorld, new Bounds(m_Center, m_Size));
  295. NavMeshBuilder.CollectSources(worldBounds, m_LayerMask, m_UseGeometry, m_DefaultArea, markups, sources);
  296. }
  297. }
  298. if (m_IgnoreNavMeshAgent)
  299. sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshAgent>() != null));
  300. if (m_IgnoreNavMeshObstacle)
  301. sources.RemoveAll((x) => (x.component != null && x.component.gameObject.GetComponent<NavMeshObstacle>() != null));
  302. AppendModifierVolumes(ref sources);
  303. return sources;
  304. }
  305. static Vector3 Abs(Vector3 v)
  306. {
  307. return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z));
  308. }
  309. static Bounds GetWorldBounds(Matrix4x4 mat, Bounds bounds)
  310. {
  311. var absAxisX = Abs(mat.MultiplyVector(Vector3.right));
  312. var absAxisY = Abs(mat.MultiplyVector(Vector3.up));
  313. var absAxisZ = Abs(mat.MultiplyVector(Vector3.forward));
  314. var worldPosition = mat.MultiplyPoint(bounds.center);
  315. var worldSize = absAxisX * bounds.size.x + absAxisY * bounds.size.y + absAxisZ * bounds.size.z;
  316. return new Bounds(worldPosition, worldSize);
  317. }
  318. Bounds CalculateWorldBounds(List<NavMeshBuildSource> sources)
  319. {
  320. // Use the unscaled matrix for the NavMeshSurface
  321. Matrix4x4 worldToLocal = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
  322. worldToLocal = worldToLocal.inverse;
  323. var result = new Bounds();
  324. foreach (var src in sources)
  325. {
  326. switch (src.shape)
  327. {
  328. case NavMeshBuildSourceShape.Mesh:
  329. {
  330. var m = src.sourceObject as Mesh;
  331. result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, m.bounds));
  332. break;
  333. }
  334. case NavMeshBuildSourceShape.Terrain:
  335. {
  336. // Terrain pivot is lower/left corner - shift bounds accordingly
  337. var t = src.sourceObject as TerrainData;
  338. result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, new Bounds(0.5f * t.size, t.size)));
  339. break;
  340. }
  341. case NavMeshBuildSourceShape.Box:
  342. case NavMeshBuildSourceShape.Sphere:
  343. case NavMeshBuildSourceShape.Capsule:
  344. case NavMeshBuildSourceShape.ModifierBox:
  345. result.Encapsulate(GetWorldBounds(worldToLocal * src.transform, new Bounds(Vector3.zero, src.size)));
  346. break;
  347. }
  348. }
  349. // Inflate the bounds a bit to avoid clipping co-planar sources
  350. result.Expand(0.1f);
  351. return result;
  352. }
  353. bool HasTransformChanged()
  354. {
  355. if (m_LastPosition != transform.position) return true;
  356. if (m_LastRotation != transform.rotation) return true;
  357. return false;
  358. }
  359. void UpdateDataIfTransformChanged()
  360. {
  361. if (HasTransformChanged())
  362. {
  363. RemoveData();
  364. AddData();
  365. }
  366. }
  367. #if UNITY_EDITOR
  368. bool UnshareNavMeshAsset()
  369. {
  370. // Nothing to unshare
  371. if (m_NavMeshData == null)
  372. return false;
  373. // Prefab parent owns the asset reference
  374. var isInPreviewScene = EditorSceneManager.IsPreviewSceneObject(this);
  375. var isPersistentObject = EditorUtility.IsPersistent(this);
  376. if (isInPreviewScene || isPersistentObject)
  377. return false;
  378. // An instance can share asset reference only with its prefab parent
  379. var prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(this) as NavMeshSurface;
  380. if (prefab != null && prefab.navMeshData == navMeshData)
  381. return false;
  382. // Don't allow referencing an asset that's assigned to another surface
  383. for (var i = 0; i < s_NavMeshSurfaces.Count; ++i)
  384. {
  385. var surface = s_NavMeshSurfaces[i];
  386. if (surface != this && surface.m_NavMeshData == m_NavMeshData)
  387. return true;
  388. }
  389. // Asset is not referenced by known surfaces
  390. return false;
  391. }
  392. void OnValidate()
  393. {
  394. if (UnshareNavMeshAsset())
  395. {
  396. Debug.LogWarning("Duplicating NavMeshSurface does not duplicate the referenced navmesh data", this);
  397. m_NavMeshData = null;
  398. }
  399. var settings = NavMesh.GetSettingsByID(m_AgentTypeID);
  400. if (settings.agentTypeID != -1)
  401. {
  402. // When unchecking the override control, revert to automatic value.
  403. const float kMinVoxelSize = 0.01f;
  404. if (!m_OverrideVoxelSize)
  405. m_VoxelSize = settings.agentRadius / 3.0f;
  406. if (m_VoxelSize < kMinVoxelSize)
  407. m_VoxelSize = kMinVoxelSize;
  408. // When unchecking the override control, revert to default value.
  409. const int kMinTileSize = 16;
  410. const int kMaxTileSize = 1024;
  411. const int kDefaultTileSize = 256;
  412. if (!m_OverrideTileSize)
  413. m_TileSize = kDefaultTileSize;
  414. // Make sure tilesize is in sane range.
  415. if (m_TileSize < kMinTileSize)
  416. m_TileSize = kMinTileSize;
  417. if (m_TileSize > kMaxTileSize)
  418. m_TileSize = kMaxTileSize;
  419. }
  420. }
  421. #endif
  422. }
  423. }