Sequence的继承关系如下:

Sequence->Composite->ParentTask->Task

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

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

2.Sequence:

成员:

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

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

方法:

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

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

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

 namespace BehaviorDesigner.Runtime.Tasks
{
[TaskDescription("The sequence task is similar to an \"and\" operation. It will return failure as soon as one of its child tasks return failure. " +
"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.")]
[HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=25")]
[TaskIcon("{SkinColor}SequenceIcon.png")]
public class Sequence : Composite
{
// The index of the child that is currently running or is about to run.
private int currentChildIndex = ;
// The task status of the last child ran.
private TaskStatus executionStatus = TaskStatus.Inactive; public override int CurrentChildIndex()
{
return currentChildIndex;
} public override bool CanExecute()
{
// We can continue to execuate as long as we have children that haven't been executed and no child has returned failure.
return currentChildIndex < children.Count && executionStatus != TaskStatus.Failure;
} public override void OnChildExecuted(TaskStatus childStatus)
{
// Increase the child index and update the execution status after a child has finished running.
currentChildIndex++;
executionStatus = childStatus;
} 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 = ;
}
}
}

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

以Sequence节点为例:

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

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

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

代码如下:

BTParentTask.lua

 BTParentTask = BTTask:New();

 local this = BTParentTask;

 function this:New()
local o = {};
setmetatable(o, self);
self.__index = self;
o.currentChildIndex = ; --当前运行到第几个子节点
o.currentChildTask = nil; --当前运行的子节点
o.childTasks = {}; --子节点列表
return o;
end function this:AddChild(task)
local index = #self.childTasks + ;
task.index = index;
task.layer = self.layer + ;
task.parent = self;
task.root = self.root;
self.childTasks[index] = task;
end --是否有子节点
function this:HasChild()
if (#self.childTasks > ) then
return true;
else
return false;
end
end --获取下一个要执行的子节点
function this:GetNextChild()
if (#self.childTasks >= (self.currentChildIndex + )) then
self.currentChildIndex = self.currentChildIndex + ;
return self.childTasks[self.currentChildIndex];
end
return nil;
end --重置
function this:Reset()
self.currentChildIndex = ;
self.currentChildTask = nil;
end

BTSequence.lua

 BTSequence = BTComposite:New();

 local this = BTSequence;

 function this:New()
local o = {};
setmetatable(o, self);
self.__index = self;
o.executionStatus = BTTaskStatus.Inactive;
return o;
end function this:OnUpdate()
if (not self:HasChild()) then
return BTTaskStatus.Failure;
end if (not self.currentChildTask) then
self.currentChildTask = self:GetNextChild();
if (not self.currentChildTask) then
return BTTaskStatus.Failure;
end
end while (self.currentChildTask) do
self.executionStatus = self.currentChildTask:OnUpdate();
if (self.executionStatus == BTTaskStatus.Failure) then --结束
self:Reset();
return BTTaskStatus.Failure;
elseif (self.executionStatus == BTTaskStatus.Running) then --下一帧继续执行
return BTTaskStatus.Running;
else --继续执行下一节点
self.currentChildTask = self:GetNextChild();
end
end --所有节点执行完毕
self.executionStatus = BTTaskStatus.Success;
self:Reset();
return BTTaskStatus.Success;
end

TestBehaviorTree.lua

 TestBehaviorTree = BTBehaviorTree:New();

 local this = TestBehaviorTree;

 function this:New()
local o = {};
setmetatable(o, self);
self.__index = self;
this:Init();
return o;
end function this:Init()
local sequence = BTSequence:New();
local log = BTLog:New("hello world!");
local log2 = BTLog:New("This is a test");
sequence:AddChild(log);
sequence:AddChild(log2);
this:PushTask(sequence);
end

打印如下:

[Unity插件]Lua行为树(三):组合节点Sequence的更多相关文章

  1. [Unity插件]Lua行为树(十三):装饰节点完善

    之前介绍了组合节点中三大常用的节点:BTSequence.BTSelector和BTParallel,一般来说,这三种就够用了,可以满足很多的需求. 接下来可以完善一下装饰节点,增加几种新的节点. 1 ...

  2. [Unity插件]Lua行为树(七):行为树嵌套

    在上一篇的基础上,可以测试下行为树的嵌套,所谓的行为树嵌套,就是在一棵行为树下的某一个分支,接入另一棵行为树. 以下面这棵行为树为例: TestBehaviorTree2.lua TestBehavi ...

  3. [Unity插件]Lua行为树(六):打印树结构

    经过前面的文章,已经把行为树中的四种基本类型节点介绍了下.接下来可以整理一下,打印一下整棵行为树.注意点如下: 1.可以把BTBehaviorTree也当作一种节点,这样就可以方便地进行行为树嵌套了 ...

  4. [Unity插件]Lua行为树(二):树结构

    参考链接:https://blog.csdn.net/u012740992/article/details/79366251 在行为树中,有四种最基本的节点,其继承结构如下: Action->T ...

  5. [Unity插件]Lua行为树(十一):组合节点Parallel

    Parallel节点类似Sequence节点,不同在于Parallel会每帧执行所有的节点.当所有节点返回成功时返回成功,当其中一个节点返回失败时,返回失败并且结束所有的子节点运行. 例如说,给Seq ...

  6. [Unity插件]Lua行为树(五):装饰节点Repeater

    Repeater:重复执行子节点,直到一定次数 特点如下: 1.执行次数可以是无限循环,也可以是固定次数 2.一般来说,子节点的执行返回状态不会影响Repeater节点,但可以设置当子节点返回失败时, ...

  7. [Unity插件]Lua行为树(四):条件节点和行为节点

    条件节点和行为节点,这两种节点本身的设计比较简单,项目中编写行为树节点一般就是扩展这两种节点,而Decorator和Composite节点只需要使用内置的就足够了. 它们的继承关系如下: Condit ...

  8. [Unity插件]Lua行为树(十):通用行为和通用条件节点

    在行为树中,需要扩展的主要是行为节点和条件节点.一般来说,每当要创建一个节点时,就要新建一个节点文件.而对于一些简单的行为节点和条件节点,为了去掉新建文件的过程,可以写一个通用版本的行为节点和条件节点 ...

  9. [Unity插件]Lua行为树(九):条件节点调整

    先看一下之前的条件节点是怎么设计的: BTConditional.lua BTConditional = BTTask:New(); local this = BTConditional; this. ...

随机推荐

  1. Delphi实现树型结构

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  2. Opencv 视频保存为图像

    // 视频存为图片.cpp : 定义控制台应用程序的入口点. // /*================================================================ ...

  3. [Android] AndroidStudio + JNI(NDK)开发相关总结

    1.官方推荐JNI构建方案 从Android studio 2.2 开始,Google推荐的JNI开发构建工具是CMake而不是NDK,参考官方文档:https://developer.android ...

  4. 【转】在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

    参考 原文链接 @Transactional does not work on method level 描述 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational) ...

  5. 【正则表达式】使用正则表达式的group,查找出String中的参数值

    需求 指标基本格式: clm.{type}.{hostId}.$metricItem 示例1: // 待匹配表达式:<hostId: 为36位的UUID> summarize(clm.pm ...

  6. 《Kubernetes权威指南》笔记-Pod、容器与Node的关系

  7. 详解MySQL主从复制实战 - 基于GTID的复制

    基于GTID的复制 简介 基于GTID的复制是MySQL 5.6后新增的复制方式. GTID (global transaction identifier) 即全局事务ID, 保证了在每个在主库上提交 ...

  8. Spring4.0之四:Meta Annotation(元注解)

    Spring框架自2.0开始添加注解的支持,之后的每个版本都增加了更多的注解支持.注解为依赖注入,AOP(如事务)提供了更强大和简便的方式.这也导致你要是用一个相同的注解到许多不同的类中去.这篇文章介 ...

  9. [UE4]结构体

    只有数据变量属性,没有函数和事件

  10. vue 定义方法执行方法 获取数据 改变数据 执行方法传值 以及事件对象

    <template> <div id="app"> <!-- <img v-bind:src='url' /> <img :src= ...