AutoRepathPolicy.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. using UnityEngine;
  2. namespace Pathfinding {
  3. using Pathfinding.Util;
  4. /// <summary>
  5. /// Policy for how often to recalculate an agent's path.
  6. ///
  7. /// See: <see cref="AIBase.autoRepath"/>
  8. /// See: <see cref="AILerp.autoRepath"/>
  9. /// </summary>
  10. [System.Serializable]
  11. public class AutoRepathPolicy {
  12. /// <summary>Policy mode for how often to recalculate an agent's path.</summary>
  13. public enum Mode {
  14. /// <summary>
  15. /// Never automatically recalculate the path.
  16. /// Paths can be recalculated manually by for example calling <see cref="IAstarAI.SearchPath"/> or <see cref="IAstarAI.SetPath"/>.
  17. /// This mode is useful if you want full control of when the agent calculates its path.
  18. /// </summary>
  19. Never,
  20. /// <summary>
  21. /// Recalculate the path every <see cref="period"/> seconds.
  22. ///
  23. /// This is primarily included for historical reasons, but might be useful if you want the path recalculations to happen at a very predictable rate.
  24. /// In most cases it is recommended to use the Dynamic mode.
  25. /// </summary>
  26. EveryNSeconds,
  27. /// <summary>
  28. /// Recalculate the path at least every <see cref="maximumPeriod"/> seconds but more often if the destination moves a lot.
  29. /// This mode is recommended since it allows the agent to quickly respond to new destinations without using up a lot of CPU power to calculate paths
  30. /// when it doesn't have to.
  31. ///
  32. /// More precisely:
  33. /// Let C be a circle centered at the destination for the last calculated path with a radius equal to the distance to that point divided by <see cref="sensitivity"/>.
  34. /// If the new destination is outside that circle the path will be immediately recalculated.
  35. /// Otherwise let F be the 1 - (distance from the circle's center to the new destination divided by the circle's radius).
  36. /// So F will be 1 if the new destination is the same as the old one and 0 if it is at the circle's edge.
  37. /// Recalculate the path if the time since the last path recalculation is greater than <see cref="maximumPeriod"/> multiplied by F.
  38. ///
  39. /// Thus if the destination doesn't change the path will be recalculated every <see cref="maximumPeriod"/> seconds.
  40. /// </summary>
  41. Dynamic,
  42. }
  43. /// <summary>
  44. /// Policy to use when recalculating paths.
  45. ///
  46. /// See: <see cref="AutoRepathPolicy.Mode"/> for more details.
  47. /// </summary>
  48. public Mode mode = Mode.Dynamic;
  49. /// <summary>Number of seconds between each automatic path recalculation for Mode.EveryNSeconds</summary>
  50. [UnityEngine.Serialization.FormerlySerializedAs("interval")]
  51. public float period = 0.5f;
  52. /// <summary>
  53. /// How sensitive the agent should be to changes in its destination for Mode.Dynamic.
  54. /// A higher value means the destination has to move less for the path to be recalculated.
  55. ///
  56. /// See: <see cref="Mode"/>
  57. /// </summary>
  58. public float sensitivity = 10.0f;
  59. /// <summary>Maximum number of seconds between each automatic path recalculation for Mode.Dynamic</summary>
  60. [UnityEngine.Serialization.FormerlySerializedAs("maximumInterval")]
  61. public float maximumPeriod = 2.0f;
  62. /// <summary>If true the sensitivity will be visualized as a circle in the scene view when the game is playing</summary>
  63. public bool visualizeSensitivity = false;
  64. Vector3 lastDestination = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
  65. float lastRepathTime = float.NegativeInfinity;
  66. /// <summary>
  67. /// True if the path should be recalculated according to the policy
  68. ///
  69. /// The above parameters are relevant only if <see cref="mode"/> is <see cref="Mode.Dynamic"/>.
  70. /// </summary>
  71. /// <param name="position">The current position of the agent.</param>
  72. /// <param name="radius">The radius of the agent. You may pass 0.0 if the agent doesn't have a radius.</param>
  73. /// <param name="destination">The goal of the agent right now</param>
  74. public virtual bool ShouldRecalculatePath (Vector3 position, float radius, Vector3 destination) {
  75. if (mode == Mode.Never || float.IsPositiveInfinity(destination.x)) return false;
  76. float timeSinceLast = Time.time - lastRepathTime;
  77. if (mode == Mode.EveryNSeconds) {
  78. return timeSinceLast >= period;
  79. } else {
  80. // cost = change in destination / max(distance to destination, radius)
  81. float squaredCost = (destination - lastDestination).sqrMagnitude / Mathf.Max((position - lastDestination).sqrMagnitude, radius*radius);
  82. float fraction = squaredCost * (sensitivity*sensitivity);
  83. if (fraction > 1.0f || float.IsNaN(fraction)) return true;
  84. if (timeSinceLast >= maximumPeriod*(1 - Mathf.Sqrt(fraction))) return true;
  85. return false;
  86. }
  87. }
  88. /// <summary>Reset the runtime variables so that the policy behaves as if the game just started</summary>
  89. public virtual void Reset () {
  90. lastRepathTime = float.NegativeInfinity;
  91. }
  92. /// <summary>Must be called when a path request has been scheduled</summary>
  93. public virtual void DidRecalculatePath (Vector3 destination) {
  94. lastRepathTime = Time.time;
  95. lastDestination = destination;
  96. }
  97. public void DrawGizmos (Vector3 position, float radius) {
  98. if (visualizeSensitivity && !float.IsPositiveInfinity(lastDestination.x)) {
  99. float r = Mathf.Sqrt(Mathf.Max((position - lastDestination).sqrMagnitude, radius*radius)/(sensitivity*sensitivity));
  100. Draw.Gizmos.CircleXZ(lastDestination, r, Color.magenta);
  101. }
  102. }
  103. }
  104. }