behavior3介绍
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
behavior3介绍
behavior3go 行为树解决游戏AI的代码组织问题,由于规范了树形结构和节点类型,具有很好的扩展性。behavior3Editer的存在,只需要改改配置就能发布一种新的逻辑,很方便。
但是对节点的描述网上有很多版本,比较混乱。本文档只是就behavior3go版本的节点进行说明。
关于behavior3框架,支持很多语言:
behavior3js (java,官方维护)
behavior3py (python,官方维护)
behavior3go (go)
behavior3cpp (c++)
behavior3-lua (lua)
一、行为树节点类型
通过behavior3Editer的页面,可以看到behavior3有四种节点类型:
Composites 组合类
Decorators 装饰类型
Actions 动作类型
Conditions 条件类型
网上其他的版本说明都是这几种类型的扩展。
如下:通过源码也可以证明:
二、行为树节点Composites组合类说明
看behavior3Editer的页面可以知道,Composites组合类的节点又可以分为如下四种,我总结如下:
- MemPriority 顺序执行只要一个不是失败就返回,如果是失败则顺序执行直至最后返回失败(支持running继续执行)
- Priority 顺序执行只要一个不是失败就返回,如果是失败则顺序执行直至最后返回失败(不支持running继续执行)
- MemSequence 顺序执行,只要一个不成功就返回,如果成功就一直执行直至返回成功(支持running存储)
- Sequence顺序执行只要一个不是成功就返回,如果是成功则顺序执行直至最后返回成功(不支持running继续执行)
下面列一下两类Priority的执行源码
type MemPriority struct {
Composite
}
/**
* Open method.
* @method open
* @param {b3.Tick} tick A tick instance.
**/
func (this *MemPriority) OnOpen(tick *Tick) {
tick.Blackboard.Set("runningChild", 0, tick.GetTree().GetID(), this.GetID())
}
/**
* Tick method.
* @method tick
* @param {b3.Tick} tick A tick instance.
* @return {Constant} A state constant.
**/
func (this *MemPriority) OnTick(tick *Tick) b3.Status {
var child = tick.Blackboard.GetInt("runningChild", tick.GetTree().GetID(), this.GetID())
for i := child; i < this.GetChildCount(); i++ {
var status = this.GetChild(i).Execute(tick)
if status != b3.FAILURE {
if status == b3.RUNNING {
tick.Blackboard.Set("runningChild", i, tick.GetTree().GetID(), this.GetID())
}
return status
}
}
return b3.FAILURE
}
可以看到,在OnOpen事件中有如下代码
tick.Blackboard.Set(“runningChild”, 0, tick.GetTree().GetID(),
this.GetID())
而在OnTick事件实现中,对running状态也做了记录,在事件执行的一开始,则拿取了之前的设置。所以MemPriority是对TreeMemory的支持,也就是当子节点返回running状态时下次再当前running状态执行。
再看Priority类型节点的执行源码,两者的区别很明显。
/**
* Tick method.
* @method tick
* @param {b3.Tick} tick A tick instance.
* @return {Constant} A state constant.
**/
func (this *Priority) OnTick(tick *Tick) b3.Status {
for i := 0; i < this.GetChildCount(); i++ {
var status = this.GetChild(i).Execute(tick)
if status != b3.FAILURE {
return status
}
}
return b3.FAILURE
}
MemSequence和Sequence的区别同理。
而Sequence和Priority的区别则是最终的判定结果是success还是failure的区别
三、行为树节点Actions的说明
行为树执行的结果有四个状态
SUCCESS
FAILURE
RUNNING
ERROR
behavior3go源码预先实现了6种Actions。除了对应上面四种状态的actions外,还有log和Wait两种。
log action记录日志完事。下面上wait action的实现源码
/**
* Open method.
* @method open
* @param {Tick} tick A tick instance.
**/
func (this *Wait) OnOpen(tick *Tick) {
var startTime int64 = time.Now().UnixNano() / 1000000
tick.Blackboard.Set("startTime", startTime, tick.GetTree().GetID(), this.GetID())
}
/**
* Tick method.
* @method tick
* @param {Tick} tick A tick instance.
* @return {Constant} A state constant.
**/
func (this *Wait) OnTick(tick *Tick) b3.Status {
var currTime int64 = time.Now().UnixNano() / 1000000
var startTime = tick.Blackboard.GetInt64("startTime", tick.GetTree().GetID(), this.GetID())
//fmt.Println("wait:",this.GetTitle(),tick.GetLastSubTree(),"=>", currTime-startTime)
if currTime-startTime > this.endTime {
return b3.SUCCESS
}
return b3.RUNNING
}
Wait action在OnOpen时记录开始running的时间,在OnTick时进行判断,超过时success,否则running。此处,this.endTime的属性为milliseconds,单位是毫秒。
四、行为树节点Decorators装饰类说明
装饰类预先设置了6种节点
- Inverter 逆变器:子节点返回成功则返回失败,反正返回失败则返回成功,其他不变。
- Limiter
限制器:执行子节点的执行次数,超过maxLoop(配置项),则不再执行子节点。子节点状态无论success还是failure,都算作一次。running则不算次数。 - MaxTime
执行时间限制器:在事件OnOpen开始计时,子节点执行超过maxTime,则必返回失败,否则返回子节点的执行状态。注意,返回一定是子节点执行完成,而非超时。 - Repeater 中继器:除非子节点返回Error或者running,否则一直执行,直到超过maxLoop。
- RepeatUnitilFailure
失败中继器:除非子节点返回Error或者running或者failure,否则一直执行,直到超过maxLoop。 - RepeatUntilSuccess
成功中继器:除非子节点返回Error或者running或者success,否则一直执行,直到超过maxLoop。
哈哈,说明一下,这个单词不是我敲错了,而是源码就这样。
五、行为树节点Condition条件类说明
behavior3go并没有预先实现条件类的事件节点
六、行为树的事件类型
先上源码
type BaseWorker struct {
}
/**
* Enter method, override this to use. It is called every time a node is
* asked to execute, before the tick itself.
*
* @method enter
* @param {Tick} tick A tick instance.
**/
func (this *BaseWorker) OnEnter(tick *Tick) {
}
/**
* Open method, override this to use. It is called only before the tick
* callback and only if the not isn't closed.
*
* Note: a node will be closed if it returned `b3.RUNNING` in the tick.
*
* @method open
* @param {Tick} tick A tick instance.
**/
func (this *BaseWorker) OnOpen(tick *Tick) {
}
/**
* Tick method, override this to use. This method must contain the real
* execution of node (perform a task, call children, etc.). It is called
* every time a node is asked to execute.
*
* @method tick
* @param {Tick} tick A tick instance.
**/
func (this *BaseWorker) OnTick(tick *Tick) b3.Status {
fmt.Println("tick BaseWorker")
return b3.ERROR
}
/**
* Close method, override this to use. This method is called after the tick
* callback, and only if the tick return a state different from
* `b3.RUNNING`.
*
* @method close
* @param {Tick} tick A tick instance.
**/
func (this *BaseWorker) OnClose(tick *Tick) {
}
/**
* Exit method, override this to use. Called every time in the end of the
* execution.
*
* @method exit
* @param {Tick} tick A tick instance.
**/
func (this *BaseWorker) OnExit(tick *Tick) {
}
事件的定义很清楚:
- OnEnter
- OnOpen
- OnTick
- OnClose
- OnExit
五个事件顺序执行,除了OnOpen和OnClose有条件判断外,其他的都没有。具体见下面源码
/**
* This is the main method to propagate the tick signal to this node. This
* method calls all callbacks: `enter`, `open`, `tick`, `close`, and
* `exit`. It only opens a node if it is not already open. In the same
* way, this method only close a node if the node returned a status
* different of `b3.RUNNING`.
*
* @method _execute
* @param {Tick} tick A tick instance.
* @return {Constant} The tick state.
* @protected
**/
func (this *BaseNode) _execute(tick *Tick) b3.Status {
//fmt.Println("_execute :", this.title)
// ENTER
this._enter(tick)
// OPEN
if !tick.Blackboard.GetBool("isOpen", tick.tree.id, this.id) {
this._open(tick)
}
// TICK
var status = this._tick(tick)
// CLOSE
if status != b3.RUNNING {
this._close(tick)
}
// EXIT
this._exit(tick)
return status
}
func (this *BaseNode) Execute(tick *Tick) b3.Status {
return this._execute(tick)
}