游戏AI
工具
Behaviac —— 来自腾讯的游戏 AI 开发框架
behaviac是游戏AI的开发框架组件,也是游戏原型的快速设计工具。
主要特性
- 支持全平台,适用于客户端和服务器,助力游戏快速迭代开发
- 编辑器可以运行在PC上,操作方便直观可靠,支持实时和离线调试
- 编辑器可以导出xml,bson等多种格式,更可以导出C++,C#源码,提供最高效率
- 运行时支持全平台,有C++和C#两个版本,原生支持Unity
- 已被《天天炫斗》、《QQ飞车》、《全民突击》、《全民夺宝》、《九龙战》等游戏及其他更多预研项目使用
Serpent.AI - Game Agent Framework (Python)
Serpent.AI 中包含大量支持模块,在以游戏为开发环境时经常遇到的场景提供解决方案,同时也提供加速开发的 CLI 工具。支持 Linux、Windows 和 MacOS 。
SerpentAI 是一个 Game Agent 框架(ps:在人机对战中,为了区分玩家,通常称机器玩家为 agent ),简单而又强大。它能把任何一个游戏变成用 Python 编写的沙盒环境,供开发者在其中创造游戏 Game Agent 做实验,使用的都是开发者非常熟悉的 Python 代码。
1. 安装
安装 Anaconda 4.4.0 (Python 3.6)
1 | # 为 Serpent.AI 创建一个 Conda 虚拟环境 |
解决 ‘chcp’ 不被识别问题
看看你的PATH环境变量是否正确设置。任何系统应该在PATH上至少有以下内容:
1 | ;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem; |
第三方软件依赖
Redis
Redis 用来实现所捕获帧的内存内存储,同时也是分析事件的临时容器。
配置 WSL(可以在 Windows 10 上的 Ubuntu 的 Bash 命令提示符中运行 Redis 服务器)
1 | # 安装 Windows Subsystem for Linux |
Tesseract
Serpent.AI 内含有一些 OCR 功能,这样我们就可以从游戏帧中读取文字了,我们把这项重任交给 Tesseract 去完成
1 | # 1. 访问 https://github.com/UB-Mannheim/tesseract/wiki |
开始安装 Serpent.AI
一旦上面的所有组件都被正确安装与配置,你就可以开始安装 Serpent.AI 框架了。
1 | # 返回你原来为自己创建的 Serpent.AI 项目目录,确保你在 Conda 虚拟环境下 |
2. Hello World
游戏准备
启动路径:
1 | J:/Words/trunck/proj.win32/Debug.win32/Words/Words.exe |
运行模式选窗口模式,记下窗口的识别名称:游戏的窗口名。
创建一个游戏插件
1 | cd SerpentAI |
配置游戏插件
编辑文件:
1 | H:\SerpentAI\plugins\SerpentWordsGamePlugin\files\serpent_Words_game.py |
使用 Words 替换掉 WINDOW_NAME
使用 J:/Words/trunck/proj.win32/Debug.win32/Words/Words.exe 替换掉 EXECUTABLE_PATH
启动游戏
1 | serpent launch Words |
创建游戏代理插件
获取游戏画面帧并与之交互
1 | # 运行 serpent generate game_agent |
更加可视化的方式
1 | # 打开另一个终端,运行 serpent visual_debugger |
3. 训练一个上下文分类器
解构游戏
希望所创建的上下文分类器能识别四种不同的状态:
- main_menu 主菜单
- level_select 关卡选择
- game 游戏中
- game_over 游戏结束
启动游戏
1 | cd SerpentAI |
捕捉上下文游戏帧图像
假设我们需要从 main_menu 主菜单捕捉图像的话:
1 | # 手动进入 main_menu 主菜单界面 |
训练上下文分类器
接下来,你将执行的是你这辈子能执行的最简单的机器学习训练操作:
1 | # 解决softmax() got an unexpected keyword argument 'axis'问题 |
4. 测试
1 | cd SerpentAI |
基础概念
游戏人工智能开发之6种决策方法
0. 人工智能遵循着:感知->思考->行动
决策方法:
- 有限状态机(Finite-State Machines)
- 分层状态机(Hierarchical Finite-State Machines)
- 行为树(Behavior Trees)
- 效用系统(Utility Systems)
- 目标导向型行动计划(Goal-Oriented Action Planners)
- 分层任务网络(Hierarchical Task Networks)
1. 有限状态机
简单又快速,使用上强大、灵活、计算开销小。
状态机状态类:FSMTransition列表为将要转到的所有可能的状态。
1 | class FSMState |
每个状态还存储着FSMTransition的类,代表能从当前状态可以转到的状态
当转换条件满足时isValid()返回true,getNextState()返回将要转到的状态,onTransition()是状态之间转换的过渡
1 | class FiniteStateMachine |
最后是有限状态机类FiniteStateMachine
包含所有状态的列表states,initialState为初始状态,activeState为当前状态
1 | class FiniteStateMachine |
伪代码如下:
1 | 在activeState.transtitions中循环调用isValid(),检测是否符合达到下一状态的条件 |
2. 分层有限状态机
把多个状态机归为一层
分层有限状态机增加了一个滞后,当我们第一次进入嵌套状态->看守建筑时,历史状态H表示为初始状态,之后历史状态H表示为最近处在的一个状态
3. 行为树
行为树是树型结构的,每个节点都代表了一个行为,每个行为都可以有子行为。
所有行为都有一个先决条件,就是产生的这些行为的条件。
整个算法先从树的根部开始,然后开始检查每一个先决条件。树的每一层只可以执行一个行为,所以当一个行为正在执行,它的兄弟节点都不会被检查,但是它们的子节点还是要检查的。
伪代码:
1 | 使根节点为当前节点 |
缺点:
- 决策树做的选择并不一定是最优的,结果也不一定是我们想要的。
- 而且决策每次都要从根部往下判断选择行为节点,比状态机要耗费时间。
- 例如:一个农民要收割作物,敌人出现了,农民逃跑,逃出了距离敌人的一定范围之后,又回去收割作物,走到敌人的范围又逃出,这样来回往复,是一个弊端
4. 效用系统
当需要选择新的行为时,我们通过分数(上面说的各种程度)来选择相对最优的选择,或者加上一个随机值再选择,使得接近优选的几个选择都有一定几率(几率可根据所加随机值决定)被选中。
5. 目标导向型行动计划
GOAP是一个反向链接搜索,从要实现的目标开始,找到什么动作能实现目标,在寻找刚才动作的先决条件,一直往前推,知道达到你的当前(初始)状态。这种反向链接搜索替代了启发式的前向链接搜索。
伪代码:
1 | 把目标加到未解决事件列表 |
6. 分层任务网络
HTN也是寻找一个计划来让AI执行,原理是最高级的任务分解成更小的任务再继续分解直到我们解决问题。HTN与GOAP相反,HTN是前向链接搜索,是从当前状态一直推到目标状态,向前推直到问题解决。
我们有两种任务:原始任务和复合任务。
原始任务是可以只解决问题的任务,也就是可以直接达到目标的任务。
复合任务是高级别的任务,可以看作方法。一个方法是一组任务可以完成复合任务,这一组任务是由先决条件决定的。复合任务让HTN推断出世界并且决定该做什么动作。
伪代码:
1 | 增加根复合任务到分解列表中 |
行为树
1. 什么是行为树
行为树,英文是Behavior Tree,简称BT,是由行为节点组成的树状结构
在BT中,节点是有层次(Hierarchical)的,子节点由其父节点来控制。
每个节点的执行都有一个结果(成功Success,失败Failure或运行Running),该节点的执行结果都由其父节点来管理,从而决定接下来做什么,父节点的类型决定了不同的控制类型。
节点不需要维护向其他节点的转换,节点的模块性(Modularity)被大大增强了。
主要优势之一就是其更好的封装性和模块性,让游戏逻辑更直观,开发者不会被那些复杂的连线绕晕。
2. 例子

3号Sequence节点有3个子节点,分别是:
- 4号Condition节点
- 5号Action节点
- 6号Wait节点
而3号节点的父节点是2号的Loop节点。
先补充下各节点类型的执行逻辑:
- 序列(Sequence)节点:顺序执行所有子节点返回成功,如果某个子节点失败返回失败。
- 循环(Loop)节点:循环执行子节点到指定次数后返回成功,如果循环次数为-1,则无限循环。
- 条件(Condition)节点:根据条件的比较结果,返回成功或失败。
- 动作(Action)节点:根据动作结果返回成功,失败,或运行。
- 等待(Wait)节点:当指定的时间过去后返回成功。
执行说明:
1 | 如果4号条件节点的执行结果是成功,其父节点3号节点则继续执行5号节点(对于持续运行一段时间的Fire动作,其执行结果持续返回"运行",结束的时候返回"成功") |
当节点持续返回”运行”的时候,BT树的内部”知道”该节点是在持续”运行”的,从而在后续的执行过程中”直接”继续执行该节点,而不需要从头开始执行,直到该运行状态的节点返回”成功”或”失败”,从而继续后续的节点。从外面看,就像”阻塞”在了那个”运行”的节点上,其父节点就像不再管理,要一直等运行的子节点结束的时候,其父节点才再次接管
持续返回”运行”状态的节点固然优化了执行,但其结果就像”阻塞”了BT的执行一样,如果发生了其他”重要”的事情需要处理怎么办?
使用前置
可以添加前置附件,并且”执行时机”设为Update或Both,则在每次执行之前都会先执行前置里配置的条件
使用Parallel节点
条件,一边执行动作”,该条件作为该动作的”Guard”条件。当该条件失败的时候来结束该处于持续运行状态的动作节点
使用SelectorMonitor节点
SelectorMonitor是一个动态的选择节点,和Selector相同的是,它选择第一个success的节点,但不同的是,它不是只选择一次,而是每次执行的时候都对其子节点进行选择。
使用Event子树
任何一个BT都可以作为事件子树,作为event附加到任何的一个节点上(用鼠标拖动BT到节点)。当运行该BT的时候,如果发生了某个事件,可以通过Agent::FireEvent来触发该事件,则处于running状态的节点,从下到上都有机会检查是否需要响应该事件,如果有该事件配置,则相应的事件子树就会被触发。
3. 总结
行为树的基本概念:
- 执行每个节点都会有一个结果(成功,失败或运行)
- 子节点的执行结果由其父节点控制和管理
- 返回运行结果的节点被视作处于运行状态,处于运行状态的节点将被持续执行一直到其返回结束(成功或失败)。在其结束前,其父节点不会把控制转移到后续节点。
游戏开源项目
著名游戏相关的开源项目
1. 商业游戏
id Software · GitHub:历来各个游戏的完整代码
Unreal Tournament:用开源方式开发
2. 游戏引擎/框架
3. 渲染引擎
KlayGE游戏引擎(@龚敏敏)
OGRE – Open Source 3D Graphics Engine
4. 物理引擎
Box2D | A 2D Physics Engine for Games:Blizzard 的程序员的个人项目
bulletphysics/bullet3 · GitHub:作者曾是Havok、Sony、AMD和现在Google的程序员。
PhysX Source on GitHub
5. 其他
- paulhodge/EASTL · GitHub
所有项目使用的服务器框架:cloudwu/skynet · GitHub
2D项目使用的渲染引擎:ejoy/ejoy2d · GitHub
一个protobuf的替代品:cloudwu/sproto · GitHub
简悦科技的开源项目:ejoy/projectlist · GitHub
Wikipedia上的一些游戏开源项目列表: