[Unity插件]Lua行为树(三):组合节点Sequence

Sequence的继承关系如下:

Sequence->Composite->ParentTask->Task

上一篇已经实现了简单版本的ParentTask和Task(基于Behavior Designer的源码),那么接下来看下Composite和Sequence。

1.Composite:表明该节点是组合节点,无特殊作用。

2.Sequence:

成员:

currentChildIndex:当前运行的子节点索引

executionStatus:当前运行的子节点状态

方法:

CanExecute:当有可运行的子节点并且子节点不返回失败时,返回true

OnChildExecuted:当一个子节点运行完后调用,将当前索引指向下一个节点,同时更新当前运行的子节点状态

OnEnd:Sequence运行结束时调用,重置变量

 1 namespace BehaviorDesigner.Runtime.Tasks
 2 {
 3     [TaskDescription("The sequence task is similar to an "and" operation. It will return failure as soon as one of its child tasks return failure. " +
 4                      "If a child task returns success then it will sequentially run the next task. If all child tasks return success then it will return success.")]
 5     [HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=25")]
 6     [TaskIcon("{SkinColor}SequenceIcon.png")]
 7     public class Sequence : Composite
 8     {
 9         // The index of the child that is currently running or is about to run.
10         private int currentChildIndex = 0;
11         // The task status of the last child ran.
12         private TaskStatus executionStatus = TaskStatus.Inactive;
13 
14         public override int CurrentChildIndex()
15         {
16             return currentChildIndex;
17         }
18 
19         public override bool CanExecute()
20         {
21             // We can continue to execuate as long as we have children that haven't been executed and no child has returned failure.
22             return currentChildIndex < children.Count && executionStatus != TaskStatus.Failure;
23         }
24 
25         public override void OnChildExecuted(TaskStatus childStatus)
26         {
27             // Increase the child index and update the execution status after a child has finished running.
28             currentChildIndex++;
29             executionStatus = childStatus;
30         }
31 
32         public override void OnConditionalAbort(int childIndex)
33         {
34             // Set the current child index to the index that caused the abort
35             currentChildIndex = childIndex;
36             executionStatus = TaskStatus.Inactive;
37         }
38 
39         public override void OnEnd()
40         {
41             // All of the children have run. Reset the variables back to their starting values.
42             executionStatus = TaskStatus.Inactive;
43             currentChildIndex = 0;
44         }
45     }
46 }

当然,因为Behavior Designer源码的代码量比较多,所以不可能完全地实现一个Lua版本的BD,不过可以参考里面的一些设计思想去实现一套行为树。

以Sequence节点为例:

1.如果当前节点返回Failure,则Sequence返回Failure,结束执行Sequence

2.如果当前节点返回Running,则Sequence返回Running,下一帧继续执行当前节点

3.如果当前节点返回Success,那么Sequence将会执行下一个节点,这里会出现两种做法,一是Sequence返回Running,等下一帧再执行下一节点;二是在同一帧下继续执行。这里我采用的是第二种,因为在早期的AI中,逻辑都是在Update中进行的,也就是逻辑都在同一帧中,因此我觉得放在同一帧中执行效率会高一些。

代码如下:

BTParentTask.lua

 1 BTParentTask = BTTask:New();
 2 
 3 local this = BTParentTask;
 4 
 5 function this:New()
 6     local o = {};
 7     setmetatable(o, self);
 8     self.__index = self;
 9     o.currentChildIndex = 0;  --当前运行到第几个子节点
10     o.currentChildTask = nil; --当前运行的子节点
11     o.childTasks = {};        --子节点列表
12     return o;
13 end
14 
15 function this:AddChild(task)
16     local index = #self.childTasks + 1;
17     task.index = index;
18     task.layer = self.layer + 1;
19     task.parent = self;
20     task.root = self.root;
21     self.childTasks[index] = task;
22 end
23 
24 --是否有子节点
25 function this:HasChild()
26     if (#self.childTasks > 0) then
27         return true;
28     else
29         return false;
30     end
31 end
32 
33 --获取下一个要执行的子节点
34 function this:GetNextChild()
35     if (#self.childTasks >= (self.currentChildIndex + 1)) then
36         self.currentChildIndex = self.currentChildIndex + 1;
37         return self.childTasks[self.currentChildIndex];
38     end
39     return nil;
40 end
41 
42 --重置
43 function this:Reset()
44     self.currentChildIndex = 0;
45     self.currentChildTask = nil;
46 end

BTSequence.lua

 1 BTSequence = BTComposite:New();
 2 
 3 local this = BTSequence;
 4 
 5 function this:New()
 6     local o = {};
 7     setmetatable(o, self);
 8     self.__index = self;
 9     o.executionStatus = BTTaskStatus.Inactive;
10     return o;
11 end
12 
13 function this:OnUpdate()
14     if (not self:HasChild()) then
15         return BTTaskStatus.Failure;
16     end
17 
18     if (not self.currentChildTask) then
19         self.currentChildTask = self:GetNextChild();
20         if (not self.currentChildTask) then
21             return BTTaskStatus.Failure;
22         end
23     end
24 
25     while (self.currentChildTask) do
26         self.executionStatus = self.currentChildTask:OnUpdate();
27         if (self.executionStatus == BTTaskStatus.Failure) then --结束
28             self:Reset();
29             return BTTaskStatus.Failure;
30         elseif (self.executionStatus == BTTaskStatus.Running) then --下一帧继续执行
31             return BTTaskStatus.Running;
32         else --继续执行下一节点
33             self.currentChildTask = self:GetNextChild();
34         end
35     end
36 
37     --所有节点执行完毕
38     self.executionStatus = BTTaskStatus.Success;
39     self:Reset();
40     return BTTaskStatus.Success;
41 end

TestBehaviorTree.lua

 1 TestBehaviorTree = BTBehaviorTree:New();
 2 
 3 local this = TestBehaviorTree;
 4 
 5 function this:New()
 6     local o = {};
 7     setmetatable(o, self);
 8     self.__index = self;
 9     this:Init();
10     return o;
11 end
12 
13 function this:Init()
14     local sequence = BTSequence:New();
15     local log = BTLog:New("hello world!");
16     local log2 = BTLog:New("This is a test");
17     sequence:AddChild(log);
18     sequence:AddChild(log2);
19     this:PushTask(sequence);
20 end

打印如下:

原文地址:https://www.cnblogs.com/lyh916/p/9440129.html