From 6ee18886c8af3858de5e97599b23086823d9f320 Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 27 Jan 2021 16:30:19 +0800 Subject: =?UTF-8?q?*=E6=9B=B4=E6=96=B0Behaviour=20Designer=E7=89=88?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E5=9C=A8=E6=9C=80=E4=B8=8B=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9C=89BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/Composites/UtilitySelector.cs | 150 +++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 Client/Assets/Behavior Designer/Runtime/Composites/UtilitySelector.cs (limited to 'Client/Assets/Behavior Designer/Runtime/Composites/UtilitySelector.cs') diff --git a/Client/Assets/Behavior Designer/Runtime/Composites/UtilitySelector.cs b/Client/Assets/Behavior Designer/Runtime/Composites/UtilitySelector.cs new file mode 100644 index 00000000..23fd6a54 --- /dev/null +++ b/Client/Assets/Behavior Designer/Runtime/Composites/UtilitySelector.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; + +namespace BehaviorDesigner.Runtime.Tasks +{ + [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 " + + "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 " + + "task reevaluates its children every tick.")] + [HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=134")] + [TaskIcon("{SkinColor}UtilitySelectorIcon.png")] + public class UtilitySelector : Composite + { + // The index of the child that is currently running or is about to run. + private int currentChildIndex = 0; + // The highest utility value + private float highestUtility; + // The task status of the last child ran. + private TaskStatus executionStatus = TaskStatus.Inactive; + // Is the task being reevaluated? + private bool reevaluating; + // A list of children that can execute. + private List availableChildren = new List(); + + public override void OnStart() + { + highestUtility = float.MinValue; + + // Loop through each child task and determine its utility. The task with the highest utility will run first. + availableChildren.Clear(); + for (int i = 0; i < children.Count; ++i) { + float utility = children[i].GetUtility(); + if (utility > highestUtility) { + highestUtility = utility; + currentChildIndex = i; + } + availableChildren.Add(i); + } + } + + public override int CurrentChildIndex() + { + // The currentChildIndex is the task with the highest utility. + return currentChildIndex; + } + + public override void OnChildStarted(int childIndex) + { + // The child has started - set the execution status. + executionStatus = TaskStatus.Running; + } + + public override bool CanExecute() + { + // Continue to execute new tasks until a task returns success or there are no more children left. If reevaluating then return false + // immediately because each task doesn't need to be reevaluted. + if (executionStatus == TaskStatus.Success || executionStatus == TaskStatus.Running || reevaluating) { + return false; + } + return availableChildren.Count > 0; + } + + public override void OnChildExecuted(int childIndex, TaskStatus childStatus) + { + // The child status will be inactive immediately following an abort from OnReevaluationEnded. The status will be running if the + // child task is interrupted. Ignore the status for both of these. + if (childStatus != TaskStatus.Inactive && childStatus != TaskStatus.Running) { + executionStatus = childStatus; + // If the execution status is failure then a new task needs to be selected. Remove the current task from the available children + // and select the next highest utility child. + if (executionStatus == TaskStatus.Failure) { + availableChildren.Remove(childIndex); + + highestUtility = float.MinValue; + for (int i = 0; i < availableChildren.Count; ++i) { + float utility = children[availableChildren[i]].GetUtility(); + if (utility > highestUtility) { + highestUtility = utility; + currentChildIndex = availableChildren[i]; + } + } + } + } + } + + public override void OnConditionalAbort(int childIndex) + { + // Set the current child index to the index that caused the abort + currentChildIndex = childIndex; + executionStatus = TaskStatus.Inactive; + } + + public override void OnEnd() + { + // All of the children have run. Reset the variables back to their starting values. + executionStatus = TaskStatus.Inactive; + currentChildIndex = 0; + } + + public override TaskStatus OverrideStatus(TaskStatus status) + { + return executionStatus; + } + + // The utility selector task is a parallel task to allow the task utility to be reevaluated. The higest utility task will always run. + public override bool CanRunParallelChildren() + { + return true; + } + + // Can reevaluate to allow the task utilities to be rerun. + public override bool CanReevaluate() + { + return true; + } + + // The behavior tree wants to start reevaluating the tree. + public override bool OnReevaluationStarted() + { + // Cannot reevaluate if the task hasn't even started yet + if (executionStatus == TaskStatus.Inactive) { + return false; + } + + reevaluating = true; + return true; + } + + // Determine if a task with a higher utility exists. + public override void OnReevaluationEnded(TaskStatus status) + { + reevaluating = false; + + // Loop through all of the available children and pick the task with the highest utility. + int prevChildIndex = currentChildIndex; + highestUtility = float.MinValue; + for (int i = 0; i < availableChildren.Count; ++i) { + float utility = children[availableChildren[i]].GetUtility(); + if (utility > highestUtility) { + highestUtility = utility; + currentChildIndex = availableChildren[i]; + } + } + + // If the index is different then the current child task should be aborted and the higher utility task should be run. + if (prevChildIndex != currentChildIndex) { + BehaviorManager.instance.Interrupt(Owner, children[prevChildIndex], this); + executionStatus = TaskStatus.Inactive; + } + } + } +} \ No newline at end of file -- cgit v1.1-26-g67d0