游戏AI之感知(1)
视觉感知
视觉感知是一种常见的感知。
在许多即时战略游戏或者类DOTA游戏里,一个单位的视觉感知往往是圆形范围的。
扇形视野
当然在其他大部分俯视角游戏里,一个智能体的视觉感知应该是类似现实人眼观看的扇形范围。
对于横板游戏,可以把视野“竖”起来,检测方式无多少差别。
对于空间更加复杂的3D游戏,可能需要视锥体(立体)检测。
一个更快的技巧是照样做成扇形检测,只是再额外增加高度差检测(即看作2.5D处理)。
但是视野实际还需考虑阻挡问题,这里提供1种主流解决视野遮挡的思路:
在所在区域的所有潜在目标进行遍历,每次遍历 先判断是否在扇形范围内,
再做一条智能体到目标的射线,若射线碰到的第一个物体是该目标,则感知到该目标。
进一步的优化则可以预先“规划”好区域,构建潜在可视集(PVS),尽可能过滤不必要的目标,缩小所在区域的潜在目标数量
(例如屋外看不到房内的人,也就可以过滤掉房内的人),那么检测速度就非常快。
示例(C++):
//视野感知
class ViewPerception {
public:
//进行一次视野感知探测
void check(Vector2 position) {
//先清理上次的结果
perceptionResult.clear();
//逐个潜在目标检测
for (Object* target : potentialTargets) {
//运用简单的数学运算判断点是否在扇形范围:
//先进行距离判断是否在半径内。
Vector2 offset = target.getPosition() - position;
float distanceSq = offset.lengthSquare();
if (distanceSq > radiusSq)continue;
//look向量和射线单位向量的数量积绝对值 若大于 数量积限制,
//则证明该射线离look向量的角度 超出数量积限制的对应角度。
float dotproduct = fabs(offset.normalize().dot(look));
if (dotproduct > dotproductlimit)continue;
//最后使用射线检测第一个碰到的物体是不是目标物体
//若射线第一个碰到的物体是目标物体,则可视为 看见了该物体
if (raycast(position, target->position).result.object == target) {
perceptionResult.emplace_back(target);
}
}
}
private:
Vector2 look; //朝前的单位向量
float radiusSq; //扇形半径的平方
float dotproductlimit; //数量积限制
std::vector<Target*> perceptionResult; //感知到的目标(结果)
//....
};
TIP:判断点在圆形范围应比较距离的平方和半径的平方,每次判断就可以减少一次开方的运算。
椭圆视野
上述扇形视野有几个缺陷:
- 智能体应该能看到贴近侧方的物体(甚至能感知到贴近背后的位置)
- 智能体对于正前方向应该能看的更远
基于这些缺陷,我们加了一个圆形和狭长的扇形视野范围(紫色点为智能体):
但是这样计算量就提升了不少,一个代替方法是使用椭圆型视野(紫色点为智能体):
椭圆(任意转向)的长轴长为2a,短轴长为2b,两个焦点离圆心的距离是c和-c(而且\(c^2=a^2-b^2\))。
椭圆上任意一点到两个焦点的距离之和必等于2a,利用这个性质可推理出:
若某个点与椭圆上两个焦点距离之和小于2a,则必在椭圆内。
因此我们只要预设好常量值:
- a:取决于视野的长度
- b:取决于视野的宽度
- c:由\(c^2=a^2-b^2\)计算出
- d1,d2:在智能体位置正前方d1距离的点为焦点c1,正前方d2距离的点为焦点c2。
bool ViewPerception::checkPointInEllipse(Vector2 targetPosition){
Vector2 c1 = this->position + this->look * d1;
Vector2 c2 = this->position + this->look * d2;
if(distance(c1,targetPosition)+distance(c2,targetPosition) <= 2*a)return true;
return false;
}
椭圆型的视野不仅能解决上述缺陷,在现实中也更贴近人类视觉的模型,计算量也只略高于一个扇形视野计算。
基于分片的高性能视域搜索系统
在很多策略游戏里,视域(Line-of-Sight,简称LOS)是很重要的概念。
在典型RTS游戏里,视域分为可见区域,不可见区域,已探索(但不可见)区域,说白了就是战争迷雾机制。
为了实现视域系统,我们先把游戏世界分为一个个整齐的分片(可以是正方形网格,六边形...)。
当我们检测某个分片是否可见时,直观的做法是直接判断该分片位置是否位于玩家视野几何形状。潜在的问题是,分片越多需要检测的次数呈几何级数增长。
而更高性能的做法是:
1. 首先每个分片记录一个数值(一般是用二维数组记录),用于记录该分片是否可视。
在实现时为满足更复杂的需求可以记录额外的数据:
- 多单位视野共享,应该用一个计数,当其中一个单位不再看见该分片时,可以减少计数,而不是直接修改为不可视。
- 多方视野,应该用一个(可能多个)Byte值,其中每个位表示某方视野是否可见。这可以用在观战系统,随时屏蔽某一方或者只关注某一个玩家的视野。
- 多种视域类型,例如可见区域,不可见区域,已探索区域...则得记录枚举值。
2. 每帧将玩家的旧视野(上一帧的视野)对应的所有分片数值修改为不可视,然后根据新视野(当前帧的视野)对应的所有分片数值修改为可视。
在修改的时候,我们可以用一个LOS模板来帮助我们快速找到视野分片,并修改之。
这个LOS模板实际上就是列表数组,每一行记录该行所有视野分片的位置:
for(int i = 0 ; i < LOStemplate.size() ; ++i){
for(int j = 0 ; j < LOStemplate[i].size(); ++j){
tiles[positionX + i - offset][positionY + LOStemplate[i][j]] = true;
}
}
得益于LOS模板,我们不仅可以引入圆形LOS,还可以引入类似手电筒视野的LOS:
由于游戏里玩家可能转向,对于一些非圆形LOS,我们可以准备多个LOS模板(例如对应90°,60°,30°..方向的LOS模板):
因为LOS模板完全可以通过预计算先算出来,所以使用它的CPU开销只与它的视野分片数相关而不是与地图分片数相关,这个性能开销已经很不错了。
3. 当需要检测分片是否可见时,直接访问记录来获取。
一个技巧是,不要主动搜索,而是利用分片记录来主动通知:
例如当一个单位需要搜索视野内的一个敌人时,不是在该单位的LOS模板范围内主动遍历搜索敌人所在的分片,
而是敌人自己根据当前位置的分片数据(多方视野记录),主动通知可看到该分片的单位。
简单来说,思想是基于事件驱动而非轮询,效率也提升的相当不错。
听觉感知
听觉感知一般比较简单粗暴:一个圆形/球形范围检测,
而且一般还无需考虑阻挡问题(现实中的声音传播可近似看作无阻挡)。
另外的,听觉感知一般需要得到的信息:
- 声音来源(例如发出声音的生物)
- 声音大小和距离
通过简单的线性计算,由声音大小和距离可以计算出实际接受声音的大小。
将这个信息作为额外数据交由决策使用。
(例如一个警卫,听到太大的声音就进入敌对状态,小的声音则进入警戒状态)
示例(C++):
//听觉感知
class ListenPerception {
public:
//进行一次听力感知探测
void check(Vector2 position) {
perceptionResult.clear();
//逐个潜在声源检测
for (Voice& voice : potentialVoices) {
//判断目标点是否在圆形范围,即距离是否在半径内。
Vector3 offset = voice.getPosition() - position;
float distanceSq = offset.lengthSquare();
if (distanceSq > radiusSq)continue;
//实际声音大小会随着距离增大而衰减
float volume = voice.getVolume() / distanceSq;
perceptionResult.emplace_back(voice.getTarget(),volume);
}
}
private:
float radiusSq; //范围半径
std::vector<std::pair<Target*, float>> perceptionResult; //感知到的目标+实际声音大小(结果)
};
其它感知
这个其实应该叫杂项感知,因为一般来说,视觉感知和听力感知已经足够一个基本的智能体所需感知了。
但极少情况还可能一些智能体需要知道各种杂项信息(例如队长给警卫发送了一条无线电消息,要求警卫赶往队长所在位置支援)。
游戏AI 系列文章:https://www.cnblogs.com/KillerAery/category/1229106.html
游戏AI之感知(1)的更多相关文章
- 游戏AI之初步介绍(0)
目录 游戏AI是什么? 游戏AI和理论AI 智能的假象 (更新)游戏AI和机器学习 介绍一些游戏AI 4X游戏AI <求生之路>系列 角色扮演/沙盒游戏中的NPC 游戏AI 需要学些什么? ...
- 实现简易而强大的游戏AI——FSM,有限状态机
http://blog.friskit.me/2012/05/introduction-of-fsm/ 在很久很久以前,受限于计算机性能和图形效果,游戏往往是以玩家为唯一主动对象的,玩家发出动作,游戏 ...
- 王亮:游戏AI探索之旅——从alphago到moba游戏
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙 发表于云+社区专栏 演讲嘉宾:王亮,腾讯AI高级研究员.2013年加入腾讯,从事大数据预测以及游戏AI研发工作.目前 ...
- 游戏AI的综合设计
原地址:http://www.cnblogs.com/cocoaleaves/archive/2009/03/23/1419346.html 学校的MSTC要出杂志,第一期做游戏专题,我写了一下AI, ...
- 游戏AI系列内容 咋样才能做个有意思的AI呢
游戏AI系列内容 咋样才能做个有意思的AI呢 写在前面的话 怪物AI怎么才能做的比较有意思.其实这个命题有点大,我作为一个仅仅进入游戏行业两年接触怪物AI还不到一年的程序员来说,来谈这个话题,我想我是 ...
- 趣说游戏AI开发:对状态机的褒扬和批判
0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...
- 使用行为树(Behavior Tree)实现游戏AI
——————————————————————— 谈到游戏AI,很明显智能体拥有的知识条目越多,便显得更智能,但维护庞大数量的知识条目是个噩梦:使用有限状态机(FSM),分层有限状态机(HFSM),决策 ...
- 如何建立一个完整的游戏AI
http://blog.friskit.me/2012/04/how-to-build-a-perfect-game-ai/ 人工智能(Artificial Intelligence)在游戏中使用已经 ...
- 对弈类游戏的人工智能(4)--游戏AI的落地
前言: 对弈类游戏的智能算法, 网上资料颇多, 大同小异. 然而书上得来终觉浅, 绝知此事要躬行. 结合了自己的工程实践, 简单汇总整理下. 一方面是对当年的经典<<PC游戏编程(人机博弈 ...
随机推荐
- MapReduce的架构及原理
MapReduce是一种分布式计算模型,是Hadoop的主要组成之一,承担大批量数据的计算功能.MapReduce分为两个阶段:Map和Reduce. 一.MapReduce的架构演变 客户端向Job ...
- Vimium快捷键记录
作为一个Geek必备的技能 从今天起在这里仅记录下使用过的快捷键和心得(翻译自 ?) version: 1.59 版本不同,快捷键有所不同 1. 下载安装地址(自备梯子) Chrome商店:https ...
- 微信授权、获取用户openid-纯前端实现——jsonp跨域访问返回json数据会报错的纯前端解决办法
近来,倒霉的后台跟我说让我拿个openid做微信支付使用,寻思很简单,开始干活. 首先引导用户打开如下链接,只需要将appid修改为自己的就可以,redirect_url写你的重定向url https ...
- ELK 架构之 Logstash 和 Filebeat 安装配置
上一篇:ELK 架构之 Elasticsearch 和 Kibana 安装配置 阅读目录: 1. 环境准备 2. 安装 Logstash 3. 配置 Logstash 4. Logstash 采集的日 ...
- 令状态寄存器访问指令(MRS,MSR)
ARM中有两条指令用于在状态寄存器和通用寄存器之间传送数据. 针对32位的ARM处理器,状态寄存器就是一个32位长的寄存器.每个位的含义如下图: 分成了4部分: 1,条件标志位 N(Negative) ...
- java中Number类理解
一般我们使用数字的时候,会使用内置的数据类型,比如int.float.double.但在实际的开发当中,我们有时候会遇到需要使用数字对象,而不是数据类型的时候.为解决这个问题,java为每一种数据类型 ...
- SHELL脚本--expr命令全解
bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html expr命令可以实现数值运算.数值或字符串比较.字符串匹配.字 ...
- SSM-MyBatis-04:Mybatis中使用properties整合jdbc.properties
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------properties整合jdbc.properties首先准备好jdbc.properties,里面的key值写 ...
- Spring提取@Transactional事务注解的源码解析
声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记: 难免有错误,敬请读者谅解!!! 1.事务注解标签 <tx:annotation-driven /> 2.tx 命名空间 ...
- pdf在线加载·
https://yq.aliyun.com/articles/40197 在线例子是这个