• [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

    打印如下:

  • 相关阅读:
    eclipse乱码解决方法
    撞库攻击:一场需要用户参与的持久战
    网管把握市场需求,其实一点都不可怜 转载于 [http://tonyxiaohome.blog.51cto.com/925273/955589]
    mysql主从不同步,提示更新找不到记录
    安装完MongoDB后尝试mongod -dbpath命令为什么会一直卡在连接端口?
    mysqlslap对mysql进行压力测试
    mysqlslap: Error when connecting to server: 2001 Can't create UNIX socket (24) 解决方法
    MySQL架构
    VMWare linux 打印太多,看不到之前的记录的解决方法总结
    启动Mysql时,提示error 2002 的解决办法
  • 原文地址:https://www.cnblogs.com/lyh916/p/9440129.html
Copyright © 2020-2023  润新知