行为树学习与理解

1. 背景

 考虑到成本与风险问题, 绝大多数游戏中的AI其实并不属于真正意义上的AI, 其中并没有对应机器学习算法的设计和AI的训练, 而是按照一定规则写死的, 因此游戏AI中最常见的是有限状态机和行为树.

 有限状态机(Finite State Machine)定义了某个对象有限个状态, 通过各种条件或事件来实现状态的转换. 有限状态机的优点是理解简单, 每一种状态都是一个节点, 发生的任何事情都是节点的转换. 缺点是模块性封装性差, 当状态变多之后, 状态之间的转换变得十分复杂, 同时维护就变得极其艰难.

 这时, 行为树(Behavior Tree)就显示出了一定优势. 行为树是包含了几种节点类型的树状结构, 每一个节点都对应一个行为, 同时每一个子节点执行后都会有一个结果, 这个结果会返回到其父节点, 并且由父节点决定后续执行, 不会与其他节点进行转换, 节点的逻辑性和模块性都被大大增强. 同时树状结构制作出的的AI编辑器逻辑更加直观, 方便查看和维护.

2. 原理

 行为树中的每个子节点都会有一个返回值, 返回值分为三种类型: 成功, 运行中, 失败. 其中成功与失败表示该节点运行结果, 运行中表示该节点还在运行, 下一次调用行为树时会继续运行该节点直到返回成功或失败.

 一个最简单的行为树, 一个小兵移动到怪物身边, 攻击:
行为树学习与理解_第1张图片
Root: 根节点, 没有父节点, 只有子节点, 是一个行为树的入口, 每一次进行行为判断时都需要从这里开始
Seq: 顺序节点, 属于逻辑节点,有父节点和子节点, 后面再详细说
Move, Attack: 动作节点, 都属于行为节点, 只有父节点, 没有子节点

 执行行为树后, 小兵直接调用Move节点中的行为方法, 如果返回成功, 则继续执行Attack中的行为方法, 如果也返回成功则Seq向根节点返回成功, 进行了一次成功的移动攻击. 如果Move返回了失败, 则不会执行Attack, Seq直接向根节点返回失败. 如果Move不是直接返回成功或失败, 而是需要移动一段时间, 则会返回运行, 下一次调用行为树时直接继续执行Move中的行为方法.

简单的伪代码来了!

dis = 0
Update()
{
	//每帧调用BT
	ExecuteBT();
}

Move()
{
	dis++
	if (dis == 3)
		print("Arrived!")
		return success
	else
		print("Moving!")
		return running
}

Attack()
{
	print("Attacking!")
	return success
}

Frame1: Moving!
Frame2: Moving!
Frame3: Arrived!
		Attcking!

3.节点类型

 行为树的节点其实都是根据游戏的需求自行设计, 下面只介绍一些比较常用的逻辑和行为节点的设计思路

逻辑节点

1. 顺序节点

顺序执行子节点, 如果其中某个子节点返回失败, 则停止执行后面的子节点并返回失败, 如果所有子节点都执行成功则返回成功

2. 选择节点

顺序执行子节点, 如果其中某个子节点返回成功, 则停止执行后面的子节点并返回成功, 如果所有子节点都执行失败则返回失败

3. 并行节点

"同时"执行所有子节点, 根据所有子节点的返回值自行设计返回结果. 同时只是该节点的表现形式, 其实该节点依旧是单线程运行, 同时体现在执行过程不受子节点的返回值影响, 只有在全部子节点执行完毕后才会判断最终返回结果

4. 定时节点

类似一个定时器, 在规定时间过后才开始执行子节点

5. 限时节点

类似一个定时器, 子节点在规定时间过后如果还在运行则返回失败

6. 循环节点

循环执行子节点规定次数或自定循环截止条件(until success…)

行为节点

1.动作节点

执行一个具体的行为动作, 如上图中的移动, 攻击等

2. 条件节点

执行一个if判断语句, 结果为true时返回success, 为false时返回failure

你可能感兴趣的:(游戏开发,行为树)