AI之用行为树来实现逻辑
行为树是一种在游戏中常用的实现AI的方式,通过行为树可行图形化实现常用的程序结构,下面就来看看如何用行为树来实现逻辑。
每次执行AI时,从根节点(root)或running节点开始遍历,父节点执行子节点,子节点执行完后将结果返回父节点,父节点根据子节点的结果来决定接下来怎么做。行为树的执行过程就是查找要执行的叶子节点的过程,查找的依据就是先执行的叶子节点的结果。通常每个节点有三个状态:成功(success),失败(failure),执行中(running)。行为树的遍历实际上是一次函数调用,立即完成的,但是动作节点的执行确实一个过程,不能立即完成。这就导致动作节点在为完成之前会被多次执行,因此引入了running状态,用于表示节点正在执行,每次执行时,跳过之前已经执行完毕的节点,从上次未执行完毕的节点开始执行。但同时需要注意的是,running状态下AI不会遍历行为树,不能影响环境的变化,running节点必须是一个不可打断动作节点。
组合节点(Composite),从左往右依次执行,类似逻辑与。
a.子节点返回failure,则停止执行,返回failure,下次从根节点开始执行;
b.子节点返回running,则停止执行,返回running,下次从running节点开始执行;
c.子节点返回success,则执行下一个节点,所有节点都返回success,则返回success,下次从根节点开始执行。
其伪代码流程如下:
def sequence_node(owner, node_name): run_index = get_run_index(node_name) while run_index < len(child_list): res = child_list[run_index](owner) if res == FAILURE: set_run_index(node_name, 0) return res elif res == RUNNING: set_run_index(node_name, run_index) return res else: run_index += 1 set_run_index(node_name, 0) return SUCCESS
2.选择节点(selector)
组合节点(Composite),从左往右依次执行,类似逻辑与。
a.子节点返回success,则停止执行,返回success,下次从根节点开始执行;
b.子节点返回running,则停止执行,返回running,下次从running节点开始执行;
c.子节点返回failure,则执行下一个节点,所有节点都返回failure,则返回failure,下次从根节点开始执行。
其伪代码流程如下:
def selector_node(owner, node_name): run_index = get_run_index(node_name) while run_index < len(child_list): res = child_list[run_index](owner) if res == SUCCESS: set_run_index(node_name, 0) elif res == RUNNING: set_run_index(node_name, run_index) else: run_index += 1 set_run_index(node_name, 0) return FAILURE
3.并行节点(parallel)
组合节点(Composite),会依次执行其全部子节点,然后根据所有子节点的返回值,指定该节点的返回值。
其伪代码流程如下:
def parallel(owner, node_name): run_index_list = get_run_index(node_name) if len(run_index_list) > 0: for index in run_index_list: state_dic[index] = child_list[index](owner) else: run_index = 0 while run_index < len(child_list): state_dic[run_index] = child_list[run_index](owner) run_index += 1 if state_dic.count(SUCCESS) >= MIN_SUCCESS: return SUCCESS elif state_dic.count(FAILURE) >= MIN_FAILURE: return FAILURE else: return RUNNING
4.概率节点(probability)
组合节点,根据填写的概率权重,随机执行其一个子节点。
其伪代码流程如下:
def probability(owner, node_name): run_index = get_run_index(node_name) if get_exe_state(node_name) == STATE_NO_RUNNING: weight = random.uniform(0, total_weight) run_index = 0 for i, w in enumerate(weights): if weight < w: run_index = i break else: weight -= w res = child_list[run_index](owner) if res == RUNNING: set_run_index(node_name, run_index) set_exe_state(node_name, STATE_RUNNING) else: set_run_index(node_name, 0) set_exe_state(node_name, STATE_NO_RUNNING) return res
5.装饰节点(decorator)
中间节点,对子节点的返回值做相应的装饰,因此一定会有一个子节点。
6.条件节点(condition)
叶子节点(Leaf)
条件成立返回success,失败返回failure。
7.动作节点(acation)
叶子节点(Leaf)
返回success
组合节点都有行为树的解析器实现,使用者需要自己实现的是叶子节点。
下面使用行为树节来实现一个怪物的AI,来演示各逻辑结构如何实现。
1.顺序
顺序逻辑结构推挤用顺序节点实现,每个子节点都是动作节点。
假设,要实现的AI,先查找目标,然后攻击。
2.选择
if结构:
假设,要实现的AI,如果发现目标,则进行攻击。
if-else结构:
例如,要实现的AI,如果发现目标,则进行攻击,否则巡逻。
if-elseif结构:
例如,要实现的AI,如果发现目标,则进行攻击,如果发现掉落装备,则捡装备,否则巡逻。
3.循环
例如,要实现的AI,如果发现目标,则进行攻击,如果发现掉落装备,则捡装备,否则巡逻。
捡装备的过程是,移动到装备所在位置,然后捡装备。
在移动到装备位置的过程中,此节点返回running,直达移动到该点,才返回success。在此过程中,循环的执行该节点,判断是否移动到了指定位置。