Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NavMeshSurfaceLinkTests.cs 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. #if UNITY_EDITOR || UNITY_STANDALONE
  2. using System;
  3. using System.Collections;
  4. using NUnit.Framework;
  5. using UnityEngine;
  6. using UnityEngine.AI;
  7. using UnityEngine.TestTools;
  8. using UnityEngine.TestTools.Utils;
  9. using Object = UnityEngine.Object;
  10. namespace Unity.AI.Navigation.Tests
  11. {
  12. [TestFixture]
  13. class NavMeshSurfaceLinkTests
  14. {
  15. GameObject m_PlaneAtOrigin;
  16. GameObject m_PlaneOnTheSide;
  17. NavMeshLink m_Link;
  18. NavMeshSurface m_Surface;
  19. readonly Vector3 m_OffsetX = new(11f, 0f, 0f);
  20. readonly Vector3 m_OffsetZ = new(0f, 0f, 11f);
  21. readonly Vector3 m_DefaultEndpointOffset = new(0f, 0f, 2.5f);
  22. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  23. readonly int m_PandaTypeID = NavMesh.CreateSettings().agentTypeID;
  24. const int k_AreaTypeForPanda = 3;
  25. NavMeshAgent m_Agent;
  26. GameObject m_ExtraNavMesh;
  27. GameObject m_FarFromNavMesh;
  28. GameObject m_TempGO;
  29. GameObject m_PathfindingStart;
  30. GameObject m_PathfindingEnd;
  31. NavMeshDataInstance m_NavMeshClone;
  32. NavMeshDataInstance m_NavMeshForPanda;
  33. NavMeshSurface m_SurfaceForPanda;
  34. #endif
  35. [OneTimeSetUp]
  36. public void OneTimeSetUp()
  37. {
  38. m_PlaneAtOrigin = GameObject.CreatePrimitive(PrimitiveType.Plane);
  39. m_PlaneOnTheSide = GameObject.CreatePrimitive(PrimitiveType.Plane);
  40. m_PlaneOnTheSide.transform.position = m_OffsetX;
  41. m_PlaneOnTheSide.transform.localScale = 0.4f * Vector3.one;
  42. m_PlaneAtOrigin.transform.localScale = 0.4f * Vector3.one;
  43. m_Surface = new GameObject("Surface").AddComponent<NavMeshSurface>();
  44. m_Surface.BuildNavMesh();
  45. Assume.That(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide), Is.False);
  46. Assume.That(HasPathConnecting(m_PlaneOnTheSide, m_PlaneAtOrigin), Is.False);
  47. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  48. m_Agent = new GameObject("Agent").AddComponent<NavMeshAgent>();
  49. m_Agent.transform.position = m_PlaneOnTheSide.transform.position - 2f * Vector3.back;
  50. m_Agent.enabled = false;
  51. m_Agent.acceleration = 100f;
  52. m_Agent.speed = 10f;
  53. Assume.That(m_Agent.speed, Is.LessThan(m_OffsetX.x),
  54. "Too high of a speed causes the agent to jump straight to the path's end.");
  55. m_PathfindingStart = new GameObject("Path Start");
  56. m_PathfindingEnd = new GameObject("Path End");
  57. m_ExtraNavMesh = new GameObject("Origin of Additional NavMesh")
  58. {
  59. transform =
  60. {
  61. position = m_PlaneAtOrigin.transform.position + m_OffsetZ
  62. }
  63. };
  64. m_FarFromNavMesh = new GameObject("Position Far From NavMesh")
  65. {
  66. transform =
  67. {
  68. position = m_ExtraNavMesh.transform.position - 2f * m_OffsetX
  69. }
  70. };
  71. m_NavMeshClone = NavMesh.AddNavMeshData(m_Surface.navMeshData, m_ExtraNavMesh.transform.position, Quaternion.identity);
  72. m_SurfaceForPanda = m_Surface.gameObject.AddComponent<NavMeshSurface>();
  73. m_SurfaceForPanda.agentTypeID = m_PandaTypeID;
  74. m_SurfaceForPanda.defaultArea = k_AreaTypeForPanda;
  75. m_SurfaceForPanda.BuildNavMesh();
  76. m_SurfaceForPanda.enabled = false;
  77. #endif
  78. }
  79. [SetUp]
  80. public void SetUp()
  81. {
  82. // move m_Link to OneTimeSetup
  83. m_Link = new GameObject("Link").AddComponent<NavMeshLink>();
  84. m_Link.transform.position = Vector3.zero;
  85. m_Link.startPoint = m_PlaneAtOrigin.transform.position;
  86. m_Link.endPoint = m_PlaneOnTheSide.transform.position;
  87. m_Link.UpdateLink();
  88. Assume.That(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide), Is.True);
  89. Assume.That(HasPathConnecting(m_PlaneOnTheSide, m_PlaneAtOrigin), Is.True);
  90. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  91. if (m_Agent.isActiveAndEnabled)
  92. {
  93. m_Agent.ResetPath();
  94. m_Agent.Warp(m_PlaneOnTheSide.transform.position - 2f * Vector3.back);
  95. }
  96. m_PathfindingStart.transform.position = m_Link.transform.position + m_Link.startPoint;
  97. m_PathfindingEnd.transform.position = m_Link.transform.position + m_Link.endPoint;
  98. #endif
  99. }
  100. [TearDown]
  101. public void TearDown()
  102. {
  103. Object.DestroyImmediate(m_Link.gameObject);
  104. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  105. if (m_TempGO != null)
  106. Object.DestroyImmediate(m_TempGO);
  107. if (m_NavMeshForPanda.valid)
  108. NavMesh.RemoveNavMeshData(m_NavMeshForPanda);
  109. m_Agent.enabled = false;
  110. #endif
  111. }
  112. [OneTimeTearDown]
  113. public void OneTimeTearDown()
  114. {
  115. Object.DestroyImmediate(m_Surface.gameObject);
  116. Object.DestroyImmediate(m_PlaneAtOrigin);
  117. Object.DestroyImmediate(m_PlaneOnTheSide);
  118. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  119. Object.DestroyImmediate(m_Agent);
  120. Object.DestroyImmediate(m_ExtraNavMesh);
  121. Object.DestroyImmediate(m_FarFromNavMesh);
  122. Object.DestroyImmediate(m_PathfindingStart);
  123. Object.DestroyImmediate(m_PathfindingEnd);
  124. if (m_NavMeshClone.valid)
  125. NavMesh.RemoveNavMeshData(m_NavMeshClone);
  126. #endif
  127. }
  128. [Test]
  129. public void Link_WhenCreated_HasDefaultEndpointOffsets()
  130. {
  131. var link = m_Link.gameObject.AddComponent<NavMeshLink>();
  132. Assert.That(link.startPoint, Is.EqualTo(-m_DefaultEndpointOffset).Using(Vector3EqualityComparer.Instance),
  133. "Newly created NavMeshLink should have the start point located at an offset from the game object.");
  134. Assert.That(link.endPoint, Is.EqualTo(m_DefaultEndpointOffset).Using(Vector3EqualityComparer.Instance),
  135. "Newly created NavMeshLink should have the end point located at an offset from the game object.");
  136. }
  137. [Test]
  138. public void Link_WithValidParameters_ConnectsTwoSurfaces()
  139. {
  140. VerifyLinkConnection(ResultsIn.PathForward);
  141. }
  142. [Test]
  143. public void Link_OnUpdateLinkWhileDisabled_DoesNotEnableConnection()
  144. {
  145. m_Link.gameObject.SetActive(false);
  146. VerifyLinkConnection(ResultsIn.NoPath);
  147. m_Link.UpdateLink();
  148. VerifyLinkConnection(ResultsIn.NoPath);
  149. }
  150. [Test]
  151. public void Link_WhenPropertyAreaChanges_UpdatesConnectionImmediately()
  152. {
  153. Assume.That(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide), Is.True);
  154. m_Link.area = 4;
  155. var anyAreaExceptTheLinkArea = ~(1 << m_Link.area);
  156. Assert.IsFalse(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide, anyAreaExceptTheLinkArea));
  157. }
  158. [Test]
  159. public void Link_WhenPropertyBidirectionalSwitchedOff_UpdatesConnectionImmediatelyToOneWay()
  160. {
  161. Assume.That(HasPathConnecting(m_PlaneOnTheSide, m_PlaneAtOrigin), Is.True);
  162. m_Link.bidirectional = false;
  163. VerifyLinkConnection(ResultsIn.PathOnlyForward);
  164. }
  165. [Test]
  166. public void Link_WhenPropertyBidirectionalSwitchedOn_UpdatesConnectionImmediatelyToBothWays()
  167. {
  168. m_Link.bidirectional = false;
  169. Assume.That(HasPathConnecting(m_PlaneOnTheSide, m_PlaneAtOrigin), Is.False);
  170. m_Link.bidirectional = true;
  171. VerifyLinkConnection(ResultsIn.PathBothWays);
  172. }
  173. [Test]
  174. public void Link_WhenPropertyCostModifierChanges_UpdatesConnectionImmediately()
  175. {
  176. m_Link.gameObject.SetActive(false);
  177. var fartherLink = m_Link.gameObject.AddComponent<NavMeshLink>();
  178. fartherLink.startPoint = m_PlaneAtOrigin.transform.position + Vector3.forward;
  179. fartherLink.endPoint = m_PlaneOnTheSide.transform.position + Vector3.forward;
  180. m_Link.gameObject.SetActive(true);
  181. Assume.That(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_Link.endPoint), Is.True);
  182. m_Link.costModifier = 1000f;
  183. Assert.IsFalse(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_Link.endPoint),
  184. "A path should not go through the connection with the higher cost, even if it's closer.");
  185. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, fartherLink.endPoint),
  186. "A path should go through the connection with the lower cost, even if it's farther away.");
  187. }
  188. [Test]
  189. public void Link_WhenPropertyStartPointChanged_UpdatesConnectionImmediately()
  190. {
  191. m_Link.startPoint = m_OffsetZ;
  192. m_PathfindingStart.transform.position = m_ExtraNavMesh.transform.position;
  193. VerifyLinkConnection(ResultsIn.PathForward);
  194. }
  195. [Test]
  196. public void Link_WhenPropertyEndPointChanged_UpdatesConnectionImmediately()
  197. {
  198. m_Link.endPoint = m_OffsetZ;
  199. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  200. VerifyLinkConnection(ResultsIn.PathForward);
  201. }
  202. [Test]
  203. public void Link_WhenPropertyWidthChanges_UpdatesConnectionImmediately()
  204. {
  205. m_Link.transform.position = 3f * Vector3.forward;
  206. m_Link.UpdateLink();
  207. Assume.That(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide), Is.False);
  208. m_Link.width = 6f;
  209. Assert.IsTrue(HasPathConnecting(m_PlaneAtOrigin, m_PlaneOnTheSide));
  210. }
  211. #if ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK
  212. [Test]
  213. public void Link_WhenPropertyActivatedChanges_UpdatesConnectionImmediately([Values(false, true)] bool activated)
  214. {
  215. m_Link.activated = activated;
  216. var asExpected = activated ? ResultsIn.PathForward : ResultsIn.NoPath;
  217. VerifyLinkConnection(asExpected);
  218. }
  219. [Test]
  220. [Description("This behavior needed to match the legacy OffMeshLink behavior.")]
  221. public void Link_WhenStartTransformAssigned_SetsStartPointOffsetToZero()
  222. {
  223. m_TempGO = new GameObject("Link");
  224. var link = m_TempGO.AddComponent<NavMeshLink>();
  225. Assume.That(link.startPoint, Is.Not.EqualTo(Vector3.zero));
  226. link.startTransform = m_ExtraNavMesh.transform;
  227. link.UpdateLink();
  228. Assert.That(link.startPoint, Is.EqualTo(Vector3.zero),
  229. "NavMeshLink should have a zero start point after a start transform has been assigned to it.");
  230. Assume.That(link.endPoint, Is.Not.EqualTo(Vector3.zero), "End point should keep a default offset.");
  231. }
  232. [Test]
  233. [Description("This behavior needed to match the legacy OffMeshLink behavior.")]
  234. public void Link_WhenEndTransformAssigned_SetsEndPointOffsetToZero()
  235. {
  236. m_TempGO = new GameObject("Link");
  237. var link = m_TempGO.AddComponent<NavMeshLink>();
  238. Assume.That(link.endPoint, Is.Not.EqualTo(Vector3.zero));
  239. link.endTransform = m_ExtraNavMesh.transform;
  240. link.UpdateLink();
  241. Assert.That(link.endPoint, Is.EqualTo(Vector3.zero),
  242. "NavMeshLink should have a zero end point after a end transform has been assigned to it.");
  243. Assume.That(link.startPoint, Is.Not.EqualTo(Vector3.zero), "Start point should keep a default offset.");
  244. }
  245. [Test]
  246. [Description("This behavior is different than the legacy OffMeshLink behavior.")]
  247. public void Link_WhenTransformRemovedAtStart_KeepsStartPointOffsetUnchanged()
  248. {
  249. m_TempGO = new GameObject("Link");
  250. var pointBefore = new Vector3(1f, 2f, 3f);
  251. var link = m_TempGO.AddComponent<NavMeshLink>();
  252. link.startTransform = m_TempGO.transform;
  253. link.startPoint = pointBefore;
  254. link.startTransform = null;
  255. Assert.That(link.startPoint, Is.EqualTo(pointBefore),
  256. "NavMeshLink should retain the same start point after the start transform has been unassigned.");
  257. Assume.That(link.endPoint, Is.EqualTo(m_DefaultEndpointOffset),
  258. "End point should keep a default offset.");
  259. }
  260. [Test]
  261. [Description("This behavior is different than the legacy OffMeshLink behavior.")]
  262. public void Link_WhenTransformRemovedAtEnd_KeepsEndPointOffsetUnchanged()
  263. {
  264. m_TempGO = new GameObject("Link");
  265. var pointBefore = new Vector3(1f, 2f, 3f);
  266. var link = m_TempGO.AddComponent<NavMeshLink>();
  267. link.endTransform = m_TempGO.transform;
  268. link.endPoint = pointBefore;
  269. link.endTransform = null;
  270. Assert.That(link.endPoint, Is.EqualTo(pointBefore),
  271. "NavMeshLink should retain the same end point after the end transform has been unassigned.");
  272. Assume.That(link.startPoint, Is.EqualTo(-m_DefaultEndpointOffset),
  273. "Start point should keep a default offset.");
  274. }
  275. [UnityTest]
  276. public IEnumerator Link_DuringAgentTraversal_ReportsIsOccupied_OtherwiseNotOccupied()
  277. {
  278. m_Agent.enabled = true;
  279. m_Agent.SetDestination(m_PlaneAtOrigin.transform.position + Vector3.back);
  280. while (!m_Agent.isOnOffMeshLink)
  281. {
  282. Assert.IsFalse(m_Link.occupied, "Link is occupied, but the agent hasn't arrived to the link yet.");
  283. yield return null;
  284. }
  285. var framesUntilComplete = 3;
  286. while (m_Agent.isOnOffMeshLink)
  287. {
  288. Assert.IsTrue(m_Link.occupied, "Link is not occupied, but the agent is on the link.");
  289. if (--framesUntilComplete == 0)
  290. m_Agent.CompleteOffMeshLink();
  291. yield return null;
  292. }
  293. Assert.IsFalse(m_Link.occupied, "Link is occupied, but agent has left the link.");
  294. }
  295. [Test]
  296. public void Link_WhenPropertyStartTransformAssigned_UpdatesConnectionImmediately()
  297. {
  298. m_Link.startTransform = m_ExtraNavMesh.transform;
  299. m_PathfindingStart.transform.position = m_ExtraNavMesh.transform.position;
  300. VerifyLinkConnection(ResultsIn.PathForward);
  301. }
  302. [Test]
  303. public void Link_WhenPropertyEndTransformAssigned_UpdatesConnectionImmediately()
  304. {
  305. m_Link.endTransform = m_ExtraNavMesh.transform;
  306. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  307. VerifyLinkConnection(ResultsIn.PathForward);
  308. }
  309. [Test]
  310. public void Link_WhenPropertyStartTransformRemoved_UpdatesConnectionImmediately()
  311. {
  312. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  313. m_Link.startTransform = m_TempGO.transform;
  314. m_Link.UpdateLink();
  315. m_PathfindingStart.transform.position = m_ExtraNavMesh.transform.position;
  316. VerifyLinkConnection(ResultsIn.PathForward);
  317. m_Link.startTransform = null;
  318. m_PathfindingStart.transform.position = m_Link.transform.position;
  319. VerifyLinkConnection(ResultsIn.PathForward);
  320. }
  321. [Test]
  322. public void Link_WhenPropertyEndTransformRemoved_UpdatesConnectionImmediately()
  323. {
  324. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  325. m_Link.endTransform = m_TempGO.transform;
  326. m_Link.startTransform = null;
  327. m_Link.startPoint = m_PlaneOnTheSide.transform.position - m_Link.transform.position;
  328. m_Link.UpdateLink();
  329. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  330. m_PathfindingStart.transform.position = m_PlaneOnTheSide.transform.position;
  331. VerifyLinkConnection(ResultsIn.PathForward);
  332. m_Link.endTransform = null;
  333. m_PathfindingEnd.transform.position = m_Link.transform.position;
  334. VerifyLinkConnection(ResultsIn.PathForward);
  335. }
  336. [Test]
  337. public void Link_WhenPropertiesTransformAndPointAtStartChange_AppliesStartPointRelativeToStartTransform()
  338. {
  339. m_Link.startTransform = m_FarFromNavMesh.transform;
  340. m_Link.startPoint = 2f * m_OffsetX;
  341. m_PathfindingStart.transform.position = m_ExtraNavMesh.transform.position;
  342. VerifyLinkConnection(ResultsIn.PathForward);
  343. }
  344. [Test]
  345. public void Link_WhenPropertiesTransformAndPointAtEndChange_AppliesStartPointRelativeToStartTransform()
  346. {
  347. m_Link.endTransform = m_FarFromNavMesh.transform;
  348. m_Link.endPoint = 2f * m_OffsetX;
  349. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  350. VerifyLinkConnection(ResultsIn.PathForward);
  351. }
  352. [UnityTest]
  353. public IEnumerator Link_WhenAutoUpdateSwitchedOn_UpdatesOnNextFrame_AndNotBefore()
  354. {
  355. Assume.That(m_Link.autoUpdate, Is.False);
  356. m_Link.transform.rotation = Quaternion.Euler(0f, -90f, 0f);
  357. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  358. m_Link.autoUpdate = true;
  359. VerifyLinkConnection(ResultsIn.NoPath);
  360. yield return null;
  361. VerifyLinkConnection(ResultsIn.PathForward);
  362. }
  363. [UnityTest]
  364. public IEnumerator Link_WhenAutoUpdateSwitchedOff_DoesNotApplyQueuedOrFutureChanges()
  365. {
  366. m_Link.autoUpdate = true;
  367. yield return null;
  368. m_Link.transform.rotation = Quaternion.Euler(0f, -90f, 0f);
  369. Assume.That(m_PathfindingEnd.transform.position, Is.EqualTo(m_PlaneOnTheSide.transform.position).Using(Vector3EqualityComparer.Instance));
  370. VerifyLinkConnection(ResultsIn.PathForward);
  371. m_Link.autoUpdate = false;
  372. yield return null;
  373. VerifyLinkConnection(ResultsIn.PathForward);
  374. m_Link.transform.SetPositionAndRotation(m_ExtraNavMesh.transform.position, Quaternion.identity);
  375. yield return null;
  376. VerifyLinkConnection(ResultsIn.PathForward);
  377. }
  378. [Test]
  379. public void Link_OnUpdateLink_AppliesChangesImmediately()
  380. {
  381. m_Link.transform.rotation = Quaternion.Euler(0f, -90f, 0f);
  382. m_Link.UpdateLink();
  383. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  384. VerifyLinkConnection(ResultsIn.PathForward, m_Link.area, m_Link.agentTypeID);
  385. }
  386. [Test]
  387. public void Link_WhenEnabled_AppliesChangesImmediately()
  388. {
  389. m_Link.enabled = false;
  390. VerifyLinkConnection(ResultsIn.NoPath);
  391. AddNavMeshForPanda();
  392. ReconfigureLinkForPanda(m_Link);
  393. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  394. VerifyLinkConnection(ResultsIn.NoPath, m_Link.area, m_Link.agentTypeID);
  395. m_Link.enabled = true;
  396. VerifyLinkConnection(ResultsIn.PathOnlyForward, m_Link.area, m_Link.agentTypeID);
  397. }
  398. void ReconfigureLinkForPanda(NavMeshLink link)
  399. {
  400. var wasEnabled = link.enabled;
  401. link.enabled = false;
  402. link.agentTypeID = m_PandaTypeID;
  403. link.area = k_AreaTypeForPanda;
  404. link.bidirectional = false;
  405. link.costModifier = 3f;
  406. link.width = 6f;
  407. link.startTransform = m_ExtraNavMesh.transform;
  408. link.endTransform = m_ExtraNavMesh.transform;
  409. link.startPoint = -m_OffsetZ + 3f * Vector3.right;
  410. link.endPoint = Vector3.zero + 3f * Vector3.right;
  411. link.enabled = wasEnabled;
  412. }
  413. void AddNavMeshForPanda()
  414. {
  415. m_NavMeshForPanda = NavMesh.AddNavMeshData(m_SurfaceForPanda.navMeshData,
  416. m_ExtraNavMesh.transform.position + 0.05f * Vector3.up, Quaternion.Euler(0f, 90f, 0f));
  417. }
  418. #endif
  419. [Test]
  420. public void Link_WhenGameObjectTransformMoves_EndpointsMoveRelativeToLinkOnUpdate()
  421. {
  422. m_Link.transform.position += Vector3.forward;
  423. Assert.IsFalse(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position + Vector3.forward));
  424. Assert.IsFalse(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position + Vector3.forward));
  425. m_Link.UpdateLink();
  426. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position + Vector3.forward));
  427. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position + Vector3.forward));
  428. }
  429. [UnityTest]
  430. public IEnumerator LinkWithAutoUpdateOn_WhenGameObjectMoves_UpdatesConnectionNextFrame()
  431. {
  432. m_Link.autoUpdate = true;
  433. m_Link.transform.position += Vector3.forward;
  434. Assert.IsFalse(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position + Vector3.forward));
  435. Assert.IsFalse(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position + Vector3.forward));
  436. yield return null;
  437. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position + Vector3.forward));
  438. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position + Vector3.forward));
  439. }
  440. [UnityTest]
  441. public IEnumerator LinkWithAutoUpdateOff_WhenGameObjectMoves_KeepsConnectionUnchanged()
  442. {
  443. m_Link.autoUpdate = false;
  444. Assume.That(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position), Is.True);
  445. Assert.That(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position), Is.True);
  446. m_Link.transform.position += Vector3.forward;
  447. // Skip a few frames
  448. yield return null;
  449. yield return null;
  450. yield return null;
  451. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneAtOrigin.transform.position));
  452. Assert.IsTrue(HasPathConnectingViaPoint(m_PlaneAtOrigin, m_PlaneOnTheSide, m_PlaneOnTheSide.transform.position));
  453. }
  454. [UnityTest]
  455. public IEnumerator LinkWithAutoUpdateOn_WhenGameObjectRotates_UpdatesConnectionNextFrame()
  456. {
  457. m_Link.autoUpdate = true;
  458. m_Link.transform.rotation = Quaternion.Euler(0f, -90f, 0f);
  459. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  460. VerifyLinkConnection(ResultsIn.NoPath);
  461. yield return null;
  462. VerifyLinkConnection(ResultsIn.PathForward);
  463. }
  464. [UnityTest]
  465. public IEnumerator LinkWithAutoUpdateOff_WhenGameObjectRotates_KeepsConnectionUnchanged()
  466. {
  467. m_Link.autoUpdate = false;
  468. m_Link.transform.rotation = Quaternion.Euler(0f, -90f, 0f);
  469. // Skip a few frames
  470. yield return null;
  471. yield return null;
  472. yield return null;
  473. Assume.That(m_PathfindingEnd.transform.position, Is.EqualTo(m_PlaneOnTheSide.transform.position).Using(Vector3EqualityComparer.Instance));
  474. VerifyLinkConnection(ResultsIn.PathForward);
  475. }
  476. [UnityTest]
  477. public IEnumerator LinkWithAutoUpdateOn_WhenTransformAtStartMoves_UpdatesConnectionNextFrame()
  478. {
  479. m_TempGO = Object.Instantiate(m_FarFromNavMesh);
  480. m_Link.startTransform = m_TempGO.transform;
  481. m_Link.UpdateLink();
  482. m_Link.autoUpdate = true;
  483. m_TempGO.transform.position += 2f * m_OffsetX;
  484. m_PathfindingStart.transform.position = m_TempGO.transform.position;
  485. VerifyLinkConnection(ResultsIn.NoPath);
  486. yield return null;
  487. VerifyLinkConnection(ResultsIn.PathForward);
  488. }
  489. [UnityTest]
  490. public IEnumerator LinkWithAutoUpdateOn_WhenTransformAtEndMoves_UpdatesConnectionNextFrame()
  491. {
  492. m_TempGO = Object.Instantiate(m_FarFromNavMesh);
  493. m_Link.endTransform = m_TempGO.transform;
  494. m_Link.UpdateLink();
  495. m_Link.autoUpdate = true;
  496. m_TempGO.transform.position += 2f * m_OffsetX;
  497. m_PathfindingEnd.transform.position = m_TempGO.transform.position;
  498. VerifyLinkConnection(ResultsIn.NoPath);
  499. yield return null;
  500. VerifyLinkConnection(ResultsIn.PathForward);
  501. }
  502. [UnityTest]
  503. public IEnumerator LinkWithAutoUpdateOn_WhenTransformAtStartDestroyed_UpdatesConnectionNextFrame()
  504. {
  505. m_Link.autoUpdate = true;
  506. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  507. m_Link.startTransform = m_TempGO.transform;
  508. m_Link.UpdateLink();
  509. m_PathfindingStart.transform.position = m_TempGO.transform.position;
  510. Object.DestroyImmediate(m_TempGO);
  511. VerifyLinkConnection(ResultsIn.PathForward);
  512. yield return null;
  513. VerifyLinkConnection(ResultsIn.NoPath);
  514. m_PathfindingStart.transform.position = m_Link.transform.position;
  515. VerifyLinkConnection(ResultsIn.PathForward);
  516. }
  517. [UnityTest]
  518. public IEnumerator LinkWithAutoUpdateOn_WhenTransformAtEndDestroyed_UpdatesConnectionNextFrame()
  519. {
  520. m_Link.autoUpdate = true;
  521. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  522. m_Link.endTransform = m_TempGO.transform;
  523. m_Link.startTransform = null;
  524. m_Link.startPoint = m_PlaneOnTheSide.transform.position - m_Link.transform.position;
  525. m_Link.UpdateLink();
  526. m_PathfindingEnd.transform.position = m_ExtraNavMesh.transform.position;
  527. m_PathfindingStart.transform.position = m_PlaneOnTheSide.transform.position;
  528. Object.DestroyImmediate(m_TempGO);
  529. VerifyLinkConnection(ResultsIn.PathForward);
  530. yield return null;
  531. VerifyLinkConnection(ResultsIn.NoPath);
  532. m_PathfindingEnd.transform.position = m_Link.transform.position;
  533. VerifyLinkConnection(ResultsIn.PathForward);
  534. }
  535. [UnityTest]
  536. public IEnumerator LinkWithAutoUpdateOff_WhenTransformAtStartMoves_KeepsConnectionUnchanged()
  537. {
  538. m_Link.autoUpdate = false;
  539. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  540. m_Link.startTransform = m_TempGO.transform;
  541. m_Link.UpdateLink();
  542. m_PathfindingStart.transform.position = m_TempGO.transform.position;
  543. VerifyLinkConnection(ResultsIn.PathForward);
  544. m_TempGO.transform.position += m_OffsetX;
  545. yield return null;
  546. yield return null;
  547. yield return null;
  548. VerifyLinkConnection(ResultsIn.PathForward);
  549. m_PathfindingStart.transform.position = m_TempGO.transform.position;
  550. VerifyLinkConnection(ResultsIn.NoPath);
  551. }
  552. [UnityTest]
  553. public IEnumerator LinkWithAutoUpdateOff_WhenTransformAtEndMoves_KeepsConnectionUnchanged()
  554. {
  555. m_Link.autoUpdate = false;
  556. m_TempGO = Object.Instantiate(m_ExtraNavMesh);
  557. m_Link.endTransform = m_TempGO.transform;
  558. m_Link.UpdateLink();
  559. m_PathfindingEnd.transform.position = m_TempGO.transform.position;
  560. VerifyLinkConnection(ResultsIn.PathForward);
  561. m_TempGO.transform.position += m_OffsetX;
  562. yield return null;
  563. yield return null;
  564. yield return null;
  565. VerifyLinkConnection(ResultsIn.PathForward);
  566. m_PathfindingEnd.transform.position = m_TempGO.transform.position;
  567. VerifyLinkConnection(ResultsIn.NoPath);
  568. }
  569. internal enum ResultsIn
  570. {
  571. PathForward,
  572. PathOnlyForward,
  573. PathBothWays,
  574. NoPath
  575. }
  576. void VerifyLinkConnection(ResultsIn expected,
  577. int areaType = 0,
  578. int agentTypeID = 0)
  579. {
  580. var forwardWanted = expected != ResultsIn.NoPath;
  581. try
  582. {
  583. Assert.That(HasPathConnecting(m_PathfindingStart, m_PathfindingEnd, 1 << areaType, agentTypeID), Is.EqualTo(forwardWanted),
  584. forwardWanted ? "The NavMesh patches should be connected." : "The NavMesh patches should not be connected.");
  585. }
  586. catch (AssertionException)
  587. {
  588. Debug.Log($"The NavMesh patches at {m_PathfindingStart.transform.position} and {m_PathfindingEnd.transform.position} should {(forwardWanted ? "" : "not ")}be connected (agent={NavMesh.GetSettingsNameFromID(agentTypeID)}, area={areaType}).");
  589. throw;
  590. }
  591. if (expected != ResultsIn.PathForward && expected != ResultsIn.NoPath)
  592. {
  593. var backwardWanted = expected == ResultsIn.PathBothWays;
  594. try
  595. {
  596. Assert.That(HasPathConnecting(m_PathfindingEnd, m_PathfindingStart, 1 << areaType, agentTypeID), Is.EqualTo(backwardWanted),
  597. backwardWanted ? "The NavMesh patches should be connected backward." : "The NavMesh patches should not be connected backward.");
  598. }
  599. catch (AssertionException)
  600. {
  601. Debug.Log($"The NavMesh patches at {m_PathfindingStart.transform.position} and {m_PathfindingEnd.transform.position} should {(backwardWanted ? "" : "not ")}be connected backward (agent={NavMesh.GetSettingsNameFromID(agentTypeID)}, area={areaType}).");
  602. throw;
  603. }
  604. }
  605. }
  606. static bool HasPathConnecting(GameObject a, GameObject b, int areaMask = NavMesh.AllAreas, int agentTypeID = 0)
  607. {
  608. var path = new NavMeshPath();
  609. var filter = new NavMeshQueryFilter
  610. {
  611. areaMask = areaMask,
  612. agentTypeID = agentTypeID
  613. };
  614. NavMesh.CalculatePath(a.transform.position, b.transform.position, filter, path);
  615. return path.status == NavMeshPathStatus.PathComplete;
  616. }
  617. static bool HasPathConnectingViaPoint(GameObject a, GameObject b, Vector3 point, int areaMask = NavMesh.AllAreas, int agentTypeID = 0)
  618. {
  619. var path = new NavMeshPath();
  620. var filter = new NavMeshQueryFilter
  621. {
  622. areaMask = areaMask,
  623. agentTypeID = agentTypeID
  624. };
  625. NavMesh.CalculatePath(a.transform.position, b.transform.position, filter, path);
  626. if (path.status != NavMeshPathStatus.PathComplete)
  627. return false;
  628. var pathCorners = path.corners;
  629. for (var i = 1; i < pathCorners.Length - 1; i++)
  630. {
  631. var corner = pathCorners[i];
  632. if (Vector3.Distance(corner, point) < 0.1f)
  633. return true;
  634. }
  635. return false;
  636. }
  637. }
  638. }
  639. #endif