149 lines
6.2 KiB
C#
149 lines
6.2 KiB
C#
![]() |
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.")]
|
|||
|
[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<int> availableChildren = new List<int>();
|
|||
|
|
|||
|
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, TaskStatus.Failure);
|
|||
|
executionStatus = TaskStatus.Inactive;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|