UtilitySelector.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System.Collections.Generic;
  2. namespace BehaviorDesigner.Runtime.Tasks
  3. {
  4. [TaskDescription("The utility selector task evaluates the child tasks using Utility Theory AI. The child task can override the GetUtility method and return the utility value " +
  5. "at that particular time. The task with the highest utility value will be selected and the existing running task will be aborted. The utility selector " +
  6. "task reevaluates its children every tick.")]
  7. [TaskIcon("{SkinColor}UtilitySelectorIcon.png")]
  8. public class UtilitySelector : Composite
  9. {
  10. // The index of the child that is currently running or is about to run.
  11. private int currentChildIndex = 0;
  12. // The highest utility value
  13. private float highestUtility;
  14. // The task status of the last child ran.
  15. private TaskStatus executionStatus = TaskStatus.Inactive;
  16. // Is the task being reevaluated?
  17. private bool reevaluating;
  18. // A list of children that can execute.
  19. private List<int> availableChildren = new List<int>();
  20. public override void OnStart()
  21. {
  22. highestUtility = float.MinValue;
  23. // Loop through each child task and determine its utility. The task with the highest utility will run first.
  24. availableChildren.Clear();
  25. for (int i = 0; i < children.Count; ++i) {
  26. float utility = children[i].GetUtility();
  27. if (utility > highestUtility) {
  28. highestUtility = utility;
  29. currentChildIndex = i;
  30. }
  31. availableChildren.Add(i);
  32. }
  33. }
  34. public override int CurrentChildIndex()
  35. {
  36. // The currentChildIndex is the task with the highest utility.
  37. return currentChildIndex;
  38. }
  39. public override void OnChildStarted(int childIndex)
  40. {
  41. // The child has started - set the execution status.
  42. executionStatus = TaskStatus.Running;
  43. }
  44. public override bool CanExecute()
  45. {
  46. // Continue to execute new tasks until a task returns success or there are no more children left. If reevaluating then return false
  47. // immediately because each task doesn't need to be reevaluted.
  48. if (executionStatus == TaskStatus.Success || executionStatus == TaskStatus.Running || reevaluating) {
  49. return false;
  50. }
  51. return availableChildren.Count > 0;
  52. }
  53. public override void OnChildExecuted(int childIndex, TaskStatus childStatus)
  54. {
  55. // The child status will be inactive immediately following an abort from OnReevaluationEnded. The status will be running if the
  56. // child task is interrupted. Ignore the status for both of these.
  57. if (childStatus != TaskStatus.Inactive && childStatus != TaskStatus.Running) {
  58. executionStatus = childStatus;
  59. // If the execution status is failure then a new task needs to be selected. Remove the current task from the available children
  60. // and select the next highest utility child.
  61. if (executionStatus == TaskStatus.Failure) {
  62. availableChildren.Remove(childIndex);
  63. highestUtility = float.MinValue;
  64. for (int i = 0; i < availableChildren.Count; ++i) {
  65. float utility = children[availableChildren[i]].GetUtility();
  66. if (utility > highestUtility) {
  67. highestUtility = utility;
  68. currentChildIndex = availableChildren[i];
  69. }
  70. }
  71. }
  72. }
  73. }
  74. public override void OnConditionalAbort(int childIndex)
  75. {
  76. // Set the current child index to the index that caused the abort
  77. currentChildIndex = childIndex;
  78. executionStatus = TaskStatus.Inactive;
  79. }
  80. public override void OnEnd()
  81. {
  82. // All of the children have run. Reset the variables back to their starting values.
  83. executionStatus = TaskStatus.Inactive;
  84. currentChildIndex = 0;
  85. }
  86. public override TaskStatus OverrideStatus(TaskStatus status)
  87. {
  88. return executionStatus;
  89. }
  90. // The utility selector task is a parallel task to allow the task utility to be reevaluated. The higest utility task will always run.
  91. public override bool CanRunParallelChildren()
  92. {
  93. return true;
  94. }
  95. // Can reevaluate to allow the task utilities to be rerun.
  96. public override bool CanReevaluate()
  97. {
  98. return true;
  99. }
  100. // The behavior tree wants to start reevaluating the tree.
  101. public override bool OnReevaluationStarted()
  102. {
  103. // Cannot reevaluate if the task hasn't even started yet
  104. if (executionStatus == TaskStatus.Inactive) {
  105. return false;
  106. }
  107. reevaluating = true;
  108. return true;
  109. }
  110. // Determine if a task with a higher utility exists.
  111. public override void OnReevaluationEnded(TaskStatus status)
  112. {
  113. reevaluating = false;
  114. // Loop through all of the available children and pick the task with the highest utility.
  115. int prevChildIndex = currentChildIndex;
  116. highestUtility = float.MinValue;
  117. for (int i = 0; i < availableChildren.Count; ++i) {
  118. float utility = children[availableChildren[i]].GetUtility();
  119. if (utility > highestUtility) {
  120. highestUtility = utility;
  121. currentChildIndex = availableChildren[i];
  122. }
  123. }
  124. // If the index is different then the current child task should be aborted and the higher utility task should be run.
  125. if (prevChildIndex != currentChildIndex) {
  126. BehaviorManager.instance.Interrupt(Owner, children[prevChildIndex], this);
  127. executionStatus = TaskStatus.Inactive;
  128. }
  129. }
  130. }
  131. }