FunnelModifier.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using Pathfinding.Util;
  4. namespace Pathfinding {
  5. [AddComponentMenu("Pathfinding/Modifiers/Funnel")]
  6. [System.Serializable]
  7. /// <summary>
  8. /// Simplifies paths on navmesh graphs using the funnel algorithm.
  9. /// The funnel algorithm is an algorithm which can, given a path corridor with nodes in the path where the nodes have an area, like triangles, it can find the shortest path inside it.
  10. /// This makes paths on navmeshes look much cleaner and smoother.
  11. /// [Open online documentation to see images]
  12. ///
  13. /// The funnel modifier also works on grid graphs however since it only simplifies the paths within the nodes which the original path visited it may not always
  14. /// simplify the path as much as you would like it to. The <see cref="Pathfinding.RaycastModifier"/> can be a better fit for grid graphs.
  15. /// [Open online documentation to see images]
  16. ///
  17. /// See: http://digestingduck.blogspot.se/2010/03/simple-stupid-funnel-algorithm.html
  18. /// </summary>
  19. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_funnel_modifier.php")]
  20. public class FunnelModifier : MonoModifier {
  21. /// <summary>
  22. /// Determines if twists and bends should be straightened out before running the funnel algorithm.
  23. /// If the unwrap option is disabled the funnel will simply be projected onto the XZ plane.
  24. /// If the unwrap option is enabled then the funnel may be oriented arbitrarily and may have twists and bends.
  25. /// This makes it possible to support the funnel algorithm in XY space as well as in more complicated cases, such
  26. /// as on curved worlds.
  27. ///
  28. /// Note: This has a performance overhead, so if you do not need it you can disable it to improve
  29. /// performance.
  30. ///
  31. /// [Open online documentation to see images]
  32. ///
  33. /// See: <see cref="Pathfinding.Funnel.Unwrap"/> for more example images.
  34. ///
  35. /// Note: This is required if you want to use the funnel modifier for 2D games (i.e in the XY plane).
  36. /// </summary>
  37. public bool unwrap = true;
  38. /// <summary>
  39. /// Insert a vertex every time the path crosses a portal instead of only at the corners of the path.
  40. /// The resulting path will have exactly one vertex per portal if this is enabled.
  41. /// This may introduce vertices with the same position in the output (esp. in corners where many portals meet).
  42. /// [Open online documentation to see images]
  43. /// </summary>
  44. public bool splitAtEveryPortal;
  45. #if UNITY_EDITOR
  46. [UnityEditor.MenuItem("CONTEXT/Seeker/Add Funnel Modifier")]
  47. public static void AddComp (UnityEditor.MenuCommand command) {
  48. (command.context as Component).gameObject.AddComponent(typeof(FunnelModifier));
  49. }
  50. #endif
  51. public override int Order { get { return 10; } }
  52. public override void Apply (Path p) {
  53. if (p.path == null || p.path.Count == 0 || p.vectorPath == null || p.vectorPath.Count == 0) {
  54. return;
  55. }
  56. List<Vector3> funnelPath = ListPool<Vector3>.Claim();
  57. // Split the path into different parts (separated by custom links)
  58. // and run the funnel algorithm on each of them in turn
  59. var parts = Funnel.SplitIntoParts(p);
  60. if (parts.Count == 0) {
  61. // As a really special case, it might happen that the path contained only a single node
  62. // and that node was part of a custom link (e.g added by the NodeLink2 component).
  63. // In that case the SplitIntoParts method will not know what to do with it because it is
  64. // neither a link (as only 1 of the 2 nodes of the link was part of the path) nor a normal
  65. // path part. So it will skip it. This will cause it to return an empty list.
  66. // In that case we want to simply keep the original path, which is just a single point.
  67. return;
  68. }
  69. for (int i = 0; i < parts.Count; i++) {
  70. var part = parts[i];
  71. if (!part.isLink) {
  72. var portals = Funnel.ConstructFunnelPortals(p.path, part);
  73. var result = Funnel.Calculate(portals, unwrap, splitAtEveryPortal);
  74. funnelPath.AddRange(result);
  75. ListPool<Vector3>.Release(ref portals.left);
  76. ListPool<Vector3>.Release(ref portals.right);
  77. ListPool<Vector3>.Release(ref result);
  78. } else {
  79. // non-link parts will add the start/end points for the adjacent parts.
  80. // So if there is no non-link part before this one, then we need to add the start point of the link
  81. // and if there is no non-link part after this one, then we need to add the end point.
  82. if (i == 0 || parts[i-1].isLink) {
  83. funnelPath.Add(part.startPoint);
  84. }
  85. if (i == parts.Count - 1 || parts[i+1].isLink) {
  86. funnelPath.Add(part.endPoint);
  87. }
  88. }
  89. }
  90. UnityEngine.Assertions.Assert.IsTrue(funnelPath.Count >= 1);
  91. ListPool<Funnel.PathPart>.Release(ref parts);
  92. // Pool the previous vectorPath
  93. ListPool<Vector3>.Release(ref p.vectorPath);
  94. p.vectorPath = funnelPath;
  95. }
  96. }
  97. }