149 lines
6.2 KiB
C#
Raw Normal View History

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;
}
}
}
}