behavior3go行为树节点类型介绍

1 篇文章 0 订阅
1 篇文章 0 订阅

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组合类的节点又可以分为如下四种,我总结如下:

  1. MemPriority 顺序执行只要一个不是失败就返回,如果是失败则顺序执行直至最后返回失败(支持running继续执行)
  2. Priority 顺序执行只要一个不是失败就返回,如果是失败则顺序执行直至最后返回失败(不支持running继续执行)
  3. MemSequence 顺序执行,只要一个不成功就返回,如果成功就一直执行直至返回成功(支持running存储)
  4. 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种节点

  1. Inverter 逆变器:子节点返回成功则返回失败,反正返回失败则返回成功,其他不变。
  2. Limiter
    限制器:执行子节点的执行次数,超过maxLoop(配置项),则不再执行子节点。子节点状态无论success还是failure,都算作一次。running则不算次数。
  3. MaxTime
    执行时间限制器:在事件OnOpen开始计时,子节点执行超过maxTime,则必返回失败,否则返回子节点的执行状态。注意,返回一定是子节点执行完成,而非超时。
  4. Repeater 中继器:除非子节点返回Error或者running,否则一直执行,直到超过maxLoop。
  5. RepeatUnitilFailure
    失败中继器:除非子节点返回Error或者running或者failure,否则一直执行,直到超过maxLoop。
  6. 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)
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值