[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运行结束时调用,重置变量
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的更多相关文章
- [Unity插件]Lua行为树(十三):装饰节点完善
之前介绍了组合节点中三大常用的节点:BTSequence.BTSelector和BTParallel,一般来说,这三种就够用了,可以满足很多的需求. 接下来可以完善一下装饰节点,增加几种新的节点. 1 ...
- [Unity插件]Lua行为树(七):行为树嵌套
在上一篇的基础上,可以测试下行为树的嵌套,所谓的行为树嵌套,就是在一棵行为树下的某一个分支,接入另一棵行为树. 以下面这棵行为树为例: TestBehaviorTree2.lua TestBehavi ...
- [Unity插件]Lua行为树(六):打印树结构
经过前面的文章,已经把行为树中的四种基本类型节点介绍了下.接下来可以整理一下,打印一下整棵行为树.注意点如下: 1.可以把BTBehaviorTree也当作一种节点,这样就可以方便地进行行为树嵌套了 ...
- [Unity插件]Lua行为树(二):树结构
参考链接:https://blog.csdn.net/u012740992/article/details/79366251 在行为树中,有四种最基本的节点,其继承结构如下: Action->T ...
- [Unity插件]Lua行为树(十一):组合节点Parallel
Parallel节点类似Sequence节点,不同在于Parallel会每帧执行所有的节点.当所有节点返回成功时返回成功,当其中一个节点返回失败时,返回失败并且结束所有的子节点运行. 例如说,给Seq ...
- [Unity插件]Lua行为树(五):装饰节点Repeater
Repeater:重复执行子节点,直到一定次数 特点如下: 1.执行次数可以是无限循环,也可以是固定次数 2.一般来说,子节点的执行返回状态不会影响Repeater节点,但可以设置当子节点返回失败时, ...
- [Unity插件]Lua行为树(四):条件节点和行为节点
条件节点和行为节点,这两种节点本身的设计比较简单,项目中编写行为树节点一般就是扩展这两种节点,而Decorator和Composite节点只需要使用内置的就足够了. 它们的继承关系如下: Condit ...
- [Unity插件]Lua行为树(十):通用行为和通用条件节点
在行为树中,需要扩展的主要是行为节点和条件节点.一般来说,每当要创建一个节点时,就要新建一个节点文件.而对于一些简单的行为节点和条件节点,为了去掉新建文件的过程,可以写一个通用版本的行为节点和条件节点 ...
- [Unity插件]Lua行为树(九):条件节点调整
先看一下之前的条件节点是怎么设计的: BTConditional.lua BTConditional = BTTask:New(); local this = BTConditional; this. ...
随机推荐
- 禁止ajax访问shiro管理的登录页面
在使用shiro的时候,对于用户权限的管理,相信很多人都已经很熟悉了.今天,我这里简单的记录一下我自己调试过程中遇到的问题.主要是登录的操作,禁止通过ajax的方式进行访问. shiro中,登录过程拒 ...
- Ubuntu Server 与 Ubuntu Desktop之间的区别
服务器版本的内核时钟频率由桌面的100hz转为1khz,这一点是为某些服务器应用提供更好的性能和吞吐量. 除此之外,服务器内核支持SMP"对称多处理"(Symmetrical Mu ...
- NDK学习笔记(二)
花了点时间把pixeliop的部分看完了,拿到开发文档提供的案例稍事修改,把画面左半边压暗. 这个案例重点在于理清pixel_engine()函数中的坐标与scanline的关系. y代表当前正在调用 ...
- WIN10 网速问题,边下载 边逛论坛 电脑 有点卡
引用:https://www.chiphell.com/forum.php?mod=viewthread&tid=1961836&extra=page%3D1&mobile=2 ...
- <亲测>ubuntu 16.04 忘记root密码
ubuntu 16.04 忘记root密码 阅读目录 方法一 方法二 虚拟机中安装的ubuntu 16.04. 回到目录 方法一 如果用户具有sudo权限,那么直接可以运行如下命令: sudo s ...
- 关于“最小的K个数”问题
从一堆无序的数中(共n个数)找到最小的K个数,这也算是一道比较经典的题目了,关于这道题目的解法,一般有几种: 方法1:先对所有的数据进行排序,然后直接找出前K个数来,即最小的K个数.时间复杂度为O(N ...
- Solr 自定义排序[1]
需求:商品排序,无库存的商品排在后面(排序). 搜索: 默认搜索: df: 可以看出左图第1个.第 ...
- linux下recv 、send阻塞、非阻塞区别和用法
非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明: 基本概念: 阻塞IO:: socket 的阻塞模式 ...
- 《Java并发编程实战》笔记-synchronized和ReentrantLock
在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具.当震要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的.可轮询的与可中断的锁获取操作,公平队 ...
- spring AOP 之一:spring AOP功能介绍
一.AOP简介 AOP:是一种面向切面的编程范式,是一种编程思想,旨在通过分离横切关注点,提高模块化,可以跨越对象关注点.Aop的典型应用即spring的事务机制,日志记录.利用AOP可以对业务逻辑的 ...