游戏算法-游戏AI行为树,python实现
参考文章:Behavior trees for AI: How they work (gamedeveloper.com)
本文主要参考上述wei'zProject Zomboid 的开发者 Chris Simpson文章的概念,用伪代码实现代码例子
AI概述
游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计,使得游戏内的单位能够感知周围环境,并做出相应的动作表现的技术。游戏AI作为游戏玩法的一大补充,在各种游戏中都有广泛的应用,比如可以和玩家交互聊天的NPC,按照特定规则寻路的怪物,与玩家进行战斗对抗的机器人等。
目前实现游戏AI的算法有
有限状态机
AI行为树
还有其他比较少用的规则式AI,甚至神经网络等
行为树AI基本概念
游戏行为树(Behavior Trees, BT)是一种用于游戏AI的设计模式。它通过模拟行为树来描述AI的行为和决策过程,以实现更加智能和自然的游戏AI。
游戏行为树由多个节点组成,每个节点代表一个行为或决策。它们按照特定的方式连接在一起,形成一个树状结构。在行为树中,根节点是AI的起点,通过遍历子节点来决策AI的行为。

节点有以下三种状态:
成功 success
失败 failure
运行 running
行为树节点有三种主要原型
组合控制节点 -Composite:
一种将多个子节点组合在一起的节点,用于实现复杂的行为和决策逻辑
主要包括
次序节点-Sequences:并行执行多个子节点,直到所有子节点都返回True或者任意一个子节点返回False为止
选择节点-Selector:按照顺序执行子节点,当某个子节点返回success时,停止执行并返回success
修饰节点-Decorator:
一种特殊的节点,它不执行具体的行为或决策,而是修饰其它节点的行为或决策
主要包括:
逆变节点-Inverter:它可以将子节点的结果倒转,比如子节点返回了 Failure,则这个修饰节点会向上返回 Success,以此类推。
重复节点-Repeater:重复执行其子节点指定的次数或者一直重复执行,直到其子节点返success或者failure
叶节点-Leaf:
树的最末端——叶子,就是这些 AI 实际上去做事情的命令或者是做一些判断
主要包括
条件节点-Condition:判断条件是否满足,如果满足则返回success,否则返回failure
行为节点-Action:执行某个具体的动作或行为,例如移动、攻击、使用技能等

节点表示

次序节点 ->Walk to Door (Success) ->次序节点(Running) ->Open Door (Success) ->次序节点(运行中) ->Walk through Door (Success) ->次序节点(Running) ->Close Door (Success) ->次序节点(Running) -> 向次序节点的父节点返回 Success。
例如,考虑上一节中提到的逆变器装饰器:

在功能上与前面的示例相同,这里我们展示了如何使用逆变器来否定任何测试,从而为您提供一个 NOT 门。这意味着你可以大幅减少测试角色或游戏世界条件所需的节点数量。
三、伪代码实现
节点基类:
# 行为树节点基类
class BaseNode(object):def __init__(self):self.status = None # 节点的执行结果: 成功 success 失败 failure 运行 runningdef execute(self, who):# 执行pass
叶子节点:行为节点
# 叶子节点-行为节点:吃食物
class EatFoodNode(BaseNode):def __init__(self, target):super(EatFoodNode).__init__()self.target = target # 食物目标def execute(self, who):# 吃食物# who.eat_foot(self.target)self.status = "success"
# 叶子节点-行为节点:打开门
class OpenDoorNode(BaseNode):def __init__(self, target):super(OpenDoorNode).__init__()self.target = target # 打开目标def execute(self, who):# 执行打开门动作# who.open_door(self.target)self.status = "success"
叶子节点:条件节点
# 叶子节点-条件节点:检查是否饥饿
class CheckHungryNode(BaseNode):def __init__(self, hungry_val):super(CheckHungryNode).__init__()self.hungry_val = hungry_val # 生命值阈值def execute(self, who):# 检查生命值是否小于阈值if self.hungry_val > who.hungry_val:self.status = "success"else:self.status = "failure"# 叶子节点-条件节点:检查是否有食物
class CheckHasFoodNode(BaseNode):def __init__(self, food):super(CheckHasFoodNode).__init__()self.food = food # 目标食物def execute(self, who):# 检查目标距离是否小于最大距离if who.has_food(self.food):self.status = "success"else:self.status = "failure"# 叶子节点-条件节点:敌人是否在周围
class CheckEnemiesAroundNode(BaseNode):def __init__(self, enemies):super(CheckEnemiesAroundNode).__init__()self.enemies = enemies # 敌人def execute(self, who):# 敌人是否在周围if who.AroundHasEnemies(self.enemies):self.status = "success"else:self.status = "failure"
组合控制节点:序列节点
# 组合控制节点:序列节点
class SequenceNode(BaseNode):def __init__(self, children):super(SequenceNode).__init__()self.children = children # 子节点列表def execute(self, who):for child in self.children:child.execute(who)if child.status == "failure":self.status = "failure"returnself.status = "success"
组合控制节点:选择节点
# 组合控制节点:选择节点
class SelectorNode(BaseNode):def __init__(self, children):super(SelectorNode).__init__()self.children = childrendef execute(self, who):for child in self.children:child.execute(who)if child.status == "success":self.status = "success"returnself.status = "failure"
装饰节点:逆变节点
# 装饰节点,逆变节点
class NOT_DecoratorNode(BaseNode):def __init__(self, child):super(DecoratorNOT).__init__()self.child = childdef execute(self, who): status = self.child.execute(who)if status == "success"self.status = "failure"elif status == "failure":self.status = "success"
例子一:

饥饿的时候,且有食物的时候,没有敌人在周围,就吃食物
# 角色对象
class Player(object):def __init__(self):self.hungry_val = 0 # 饥饿度self.food = "fish" # 食物def main():# 首先定义行为树的结构root = SequenceNode([# 饥饿的时候CheckHungryNode(50),# 有鱼时候CheckHasFoodNode("fish"),# 敌人不在周围NOT_DecoratorNode(CheckEnemiesAroundNode("李宏伟")),# 老墨吃鱼EatFoodNode("fish"),])who = Player()# 然后在主循环中执行行为树while True:# 执行行为树root.execute(who)# 根据行为执行结果更新状态if root.status == "success":who.hungry_val = random.randint(1, 100)who.food = random.randint(1, 100)# 等待一段时间后再次执行行为树time.sleep(1)
例子二:

# 行为树的结构如下root = SequenceNode([WalkToDoor(),SelectorNode([OpenDoor(),SequenceNode([UnlockDoor(),OpenDoor("self"),]),SmashDoor(),]),WalkThroughDoor(),CloseDoor(), ])
相关文章:
游戏算法-游戏AI行为树,python实现
参考文章:Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念,用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计,使得游…...
【新2023Q2模拟题JAVA】华为OD机试 - 矩阵最值 or 计算二维矩阵的最大值
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:矩阵最值 or 计算二维矩阵的最…...
递归过程与递归工作栈
首先了解一下任意两个函数之间进行调用的情况: 比如在a函数里面调用b函数,那么在使用b函数之前,计算机还会1.把实参,返回地址给复制下来(但只是复制而已,地址不是原来的。)如图: 此外2.为被调用…...
B 树的简单认识
理解 B 树的概念 B 树是一种自平衡的查找树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除数据的动作,都能在对数时间内完成。 同一般的二叉查找树不同,B 树是一棵多路平衡查找树,其特性是ÿ…...
【大数据Hive3.x数仓开发】窗口函数案例:连续N次登录的用户;级联累加求和;分组TopN
文章目录1 统计连续N次登录的用户(N>2)自连接过滤实现窗口函数lead()实现2 级联累加求和自连接窗口函数sum()实现3 分组TopN问题对窗口函数的讲解part见:【大数据Hive3.x数仓开发】函数–窗口函数 1 统计连续N次登录的用户(N&…...
openpyxl库自动填充excel实例分享
openpyxl可以通过编写Python脚本实现自动化Excel操作,包括自动填充数据、格式化单元格、生成图表等操作。 以下是一个常见的自动化Excel操作示例: 自动填充数据: from openpyxl import Workbook from openpyxl.utils import get_column_l…...
ICLR2021清华团队做的知识蒸馏提升detector的点的工作paper 小陈读论文系列
这个作者栏目就是一个词 清爽 牛逼不需要花里胡哨哈哈 无疑是有点tian了哈哈 不重要 毕竟有机会研读 梦中情笑的paper 还是很感激的 真的 很清爽啊 很多KD的工作确实 在下游任务呢效果不是很好 然后就引出了自己的关于提升知识蒸馏在OD方面的工作 OD 首先就有两个问题 1.前…...
Java核心技术知识点笔记—集合框架
前言:Java最初版本只为最常用的数据结构提供了很少的一组类:Vector、Stack、Hashtable、BitSet和Enumeration接口。其中,Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制。与现代数据结构类库常见情况一样,Java集…...
Rsync数据同步工具
一、什么是Rsync Rsync是一款开源的,快速的,多功能的,可实现全量及增量(差异化备份)的本地或远程数据同步备份的优秀工具。 Rsync软件适用于Unix、Linux、Windows等多种操作系统。 (1)可使本地…...
redux小结
store.dispatch(action对象) 在 dispatch 中调用 action 方法返回 action 对象 // /actions/index.js /*** Action:* action本质上是一个 JS 对象;* 必须要包含 type 属性,否则会报错;* 只描述了有事情要发生,…...
【Python】【进阶篇】十、Pygame的Font文本和字体
目录十、Pygame的Font文本和字体10.1 font.SysFont()10.2 font.Font()10.3 字体对象方法十、Pygame的Font文本和字体 Pygame 通过pygame.font模块来创建一个字体对象,从而实现绘制文本的目的。 该模块的常用方法如下所示: 名称说明pygame.font.init()初…...
【从零开始学习 UVM】10.8、UVM TLM —— UVM TLM Example
文章目录 subComp1subComp2ComponentAsubComp3ComponentBTop Env/Test这个 UVM TLM 示例使用之前文章中讨论的 put 端口、TLM FIFO 和 get 端口来构建一个具有不同层次的 TLM 端口的测试台。 下面定义了一个名为Packet的类,作为从一个组件传输到另一个组件的数据项。这个类对象…...
获取自己所上传资源的下载量
import requestsurl = https://download-console-api.csdn.net/v1/user/sources/getUploadListByUserName?status=2&pageNum=1&pageSize=100 cookie = # 这里填自己的cookie header = {"authority": "download-console-api.csdn.net","met…...
Aspose.cells模板导出使用记录
简述 用Aspose.cells导出可以方便地将数据到Excel文档中,简单的直接将DataTable列表写入即可,复杂的格式一般会先做好模板,再将数据填充进去,这样可以保持设置好的样式,又能快速填充内容,十分方便。 智能…...
AcWing——糖果传递
有 n个小朋友坐成一圈,每人有 a[i]个糖果。 每人只能给左右两人传递糖果。 每人每次传递一个糖果代价为 1。 求使所有人获得均等糖果的最小代价。 输入格式 第一行输入一个正整数 n,表示小朋友的个数。 接下来 n 行,每行一个整数 a[i]&…...
Redis中的单线程模型
文章目录 文件事件处理器模型Redis的客户端与服务端的交互过程图Redis基于Reactor模式开发了自己的网络事件处理器,称之为 文件事件处理器(File Event Hanlder)。 文件事件处理器由Socket、IO多路复用程序文件事件分派器(dispather)事件处理器(handler)文件事件处理器模型 IO…...
Python函数默认参数设置(超级详细)
我们知道,在调用函数时如果不指定某个参数,Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默…...
人工智能如何赋能业务创新?安克创新有话要说
对于一家企业来说,应该如何运用人工智能技术助力业务创新?作为一家多年复合增长率超过35%的企业,安克创新对这个话题无疑有着深切的体验感悟。飞速成长的消费电子企业众所周知,当下各行各业都在如火如荼地开展人工智能应用&#x…...
如何学习与学习的本质
如何学习两种模式两种记忆方式拖延问题学习方法学习本质两种模式 专注模式发散模式 专注模式和发散模式可以进行切换,提高效率, 发散模式可以后台工作。 两种记忆方式 工作记忆(前额叶皮质)长时记忆(图像比较容易记…...
C++ deque容器
C deque容器 文章目录C deque容器前言1. deque容器基本概念2. deque构造函数3. deque赋值操作4. deque大小操作5. deque 插入和删除6. deque 数据存取7. deque 排序总结前言 本文包含deque容器基本概念、deque构造函数、deque赋值操作、deque大小操作、deque插入和删除、deque…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
