blob: 23fd6a54190a332c619bbce40609fcd6e7ee29f6 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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<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);
executionStatus = TaskStatus.Inactive;
}
}
}
}
|