灯塔AOI简易实现
首先我们来讨论下游戏开发中的几个坐标系,为了方便解释,我截取了灯塔AOI DEMO当NPC数目为0时候的样子(代码地址觉得有帮助的童鞋记得给我代码点个星_)
先对这张图简单说明下:
- 蓝色的坐标轴表示是灯塔AOI坐标系,绿色的坐标轴表示的是游戏坐标系,向左为X轴正方向,向上为Y轴正方向(这个坐标是我自己后面画上去的)
- 深蓝色的点表示灯塔AOI坐标,左下的表示(0,0),右上表示(1,1)
- 深绿色的点表示游戏坐标,左下表示(0,0),右上表示(1,1)
- 每个灰色的小格子代表一个游戏坐标(边长为15像素)
- 每个黄灰的大格子代表一个灯塔AOI坐标(边长为11个灰色小格子)
- 瓦片地图大小为宽:100个灰色小格子 高:70个灰色小格子
- 深蓝色的方块为玩家,大红色方框为玩家在游戏坐标系中的视野(视野半径为10个灰色小格子),兰色方框表示玩家在灯塔坐标系中的视野
像素
这个是客户端图形实际显示要用到的坐标(和游戏逻辑无关),每个图片长宽各多少像素,描点在什么位置,但是在游戏中直接使用像素作为计量单位(坐标点)十分不方便,比如物体的移动,况且移动也不会一个像素一个像素移动,所以我们做了一层抽象(格子),游戏相关的贴图(地图/人物/道具)必须是格子的整数倍,这样游戏中所有的物体才能被正常显示,而不会产生偏差。
格子/瓦片(游戏坐标系)
什么是格子?格子是游戏逻辑使用的最小单位,代表游戏坐标系的点,它的单位为像素(DEMO中15*15像素为一格),游戏中所有设定都必须是格子的整数倍,所以不会出现有物体在两个格子之间,导致坐标不确定的情况,物体的移动的步长单位也变成多少格,而不是像素了,即如果使用像素作为坐标点,从(0,0)移动到(0,1),则移动了1个像素,现在用格子作为坐标点,从(0,0)移动到(0,1),则移动了15个像素。
什么是瓦片?一张大的世界地图或者背景图可以由几种地形来表示,每种地形对应一张小的的图片,我们称这些小的地形图片为瓦片。把这些瓦片拼接在一起,一个完整的地图就组合出来了,这就是瓦片地图。
在DEMO中,为了简化逻辑,我将瓦片设置为一倍格子大小,玩家和NPC的大小也设置为一倍格子大小。
灯塔AOI坐标系
为什么需要灯塔AOI?假设我们想知道某点周围10格内有哪些对象,在没有灯塔AOI的情况下,我们需要遍历所有的对象计算其是否在范围内,随着地图内的对象越来越多,查找的效率也会越来越差,所以我们需要一种方法来过滤那些明显不需要参与计算的对象,所以我们将地图分割成一个个区域,在其中心放置一个假想的"灯塔",每个"灯塔"都会保存区域内的对象,这样当我们需要知道某点周围10格内有哪些对象时,我们只需要计算出范围内有哪些"灯塔",然后获取这些"灯塔"保存的对象列表,针对这些对象进行计算就能节省大量计算。为了方便表示和管理这些"灯塔",我们为其分配了新坐标(左下为(0,0)),这个新的坐标系即灯塔AOI坐标系(这个坐标系是用来做碰撞检测的)
灯塔视野和玩家视野
"灯塔"的视野越小,在碰撞检测时能过滤的无效对象就越多,但是整张地图"灯塔"的数目也就越多,消耗的内存就越大,而且对象进出灯塔的计算量就越多
"灯塔"的视野越大,在碰撞检测时能过滤的无效对象就越少,碰撞检测的计算量就越大,想象下只有一个"灯塔"的情况,即退回了没有灯塔AOI系统的情况
由于玩家的视野一般是固定的(屏幕显示区域大小一般固定),所以灯塔视野大小一般是玩家视野的1/2或1/3比较合适
灯塔坐标的计算
假设要计算游戏中某点所处的灯塔坐标(详细逻辑见代码注释):
### 将游戏坐标系和灯塔AOI坐标系对齐,然后除以灯塔边长(两倍视野内包含的格子数 + 自身坐标格子)
static_cast<int>(std::floor(static_cast<float>(游戏X坐标 - 游戏地图左下角原点X坐标) / (2 * "灯塔"视野 + 1))
static_cast<int>(std::floor(static_cast<float>(游戏Y坐标 - 游戏地图左下角原点Y坐标) / (2 * "灯塔"视野 + 1))
灯塔AOI逻辑
对象进入(角色登入、生成怪物):
- 根据对象坐标计算对象所属灯塔,将对象添加到灯塔的对象列表,如果灯塔上绑定了观察者,则通知观察者有对象进入
- 找出对象视野范围的灯塔,将自身绑定为其观察者,绑定灯塔会将自身现有对象列表发送给对象
对象离开(角色登出、怪物被杀死):
- 根据对象坐标计算对象所属灯塔,将对象从灯塔的对象列表中移除,如果灯塔上绑定了观察者,则通知观察者有对象离开
- 找出对象视野范围的灯塔,解除其观察者绑定,解除绑定灯塔会将自身现有对象列表发送给对象
对象移动
- 如果对象所属灯塔没变,则不做任何操作
- 如果对象所属灯塔改变,则对旧灯塔执行对象离开逻辑,对新灯塔执行对象进入逻辑,但是要注意的是对视野的处理,前后视野交集内的灯塔不需要执行解绑和绑定操作
灯塔AOI简易实现的更多相关文章
- [game]十字链表的AOI算法实现
AOI主要有九宫格.灯塔和十字链表的算法实现.本文阐述十字链表的实现和尝试. 1. 基本原理 根据二维地图,将其分成x轴和y轴两个链表.如果是三维地图,则还需要维护多一个z轴的链表.将对象的坐标值按照 ...
- AOI自动光学检测机技术在电路板检查中的应用
1.简述 AOI技术在许多不同的制造业领域使用,自从电子影像技术开始发展,就被各种人利用在不同的应用领域.大家最熟悉的数字相机.数字摄影机是大家生活中最常用到的器材之一,而工业产品的生产也大量使用这些 ...
- 解码mmo游戏服务器三:大地图同步(aoi)
问题引入:aoi(area of interest).在大地图中,玩家只需要关心自己周围的对象变化,而不需要关心距离较远的对象的变化.所以大地图中的数据不需要全部广播,只要同步玩家自己视野范围的消息即 ...
- AOI 设计
http://blog.csdn.net/zhanghefu/article/details/25833535 云风的Blog 并进行整理而写. AOI(Area Of Interest),中文就是感 ...
- .NET里简易实现AOP
.NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...
- 在.Net中实现自己的简易AOP
RealProxy基本代理类 RealProxy类提供代理的基本功能.这个类中有一个GetTransparentProxy方法,此方法返回当前代理实例的透明代理.这是我们AOP实现的主要依赖. 新建一 ...
- .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...
- 自己来实现一个简易的OCR
来做个简易的字符识别 ,既然是简易的 那么我们就不能用任何的第三方库 .啥谷歌的 tesseract-ocr, opencv 之类的 那些玩意是叼 至少图像处理 机器视觉这类课题对我这种高中没毕业的人 ...
- php+websocket搭建简易聊天室实践
1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...
随机推荐
- C# 通用类型转换方法
在程序开发过程中经常需要进行数据的类型转换,而且如果一个字段的类型改成另一个类型时,所有相关的类型转换的地方都要跟着修改,不但造成了很多重复转换的代码而且修改字段类型时额外修改相关转换代码的工作量也很 ...
- springmvc防止表单重复提交demo
原理:在去某个页面直接生成一个随机数(这里使用的是UUID)并放入session中,用户提交表单时将这个随机数传入服务端与session中的值进行比较,如果不不存在或不相等,则认为是重复提交:如果相等 ...
- verilog 之语法学习
1.使用非基数表示的十进制视为有符号数.使用基数表示的十进制被视为无符号数. 2.线网中的值被解释为无符号数,整型寄存器中的值被解释为有符号的二进制补码数,. 3.如果选择表达式的值为 x.z,或越界 ...
- Python Twisted系列教程11:改进诗歌下载服务器
作者:dave@http://krondo.com/your-poetry-is-served/ 译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 诗歌下载服务器 到目前为止,我们已经学习了大 ...
- 一只小蜜蜂(斐波那契dp)
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行.请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数. 其中,蜂房的结构如下所示. Input输入数据的第一行是一个整数N,表示测试实例的个数,然后是 ...
- BIO与NIO、AIO的区别(转)
IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSock ...
- 生成ssl脚本文件
read -p "Enter your domain [www.example.com]: " DOMAIN echo "Create server key...&quo ...
- __sync_fetch_and_add系列
__sync_fetch_and_add系列一共有十二个函数,有加/减/与/或/异或/等函数的原子性操作函数,__sync_fetch_and_add,顾名思义,先fetch,然后自加,返回的是自加以 ...
- 【HDU4970】Killing Monsters
题意 数轴上有n个点,有m座炮塔,每个炮塔有一个攻击范围和伤害,有k个怪物,给出他们的初始位置和血量,问最后有多少怪物能活着到达n点.n<=100000 分析 对于某个怪物,什么情况下它可以活着 ...
- 728. Self Dividing Numbers可以自己除以自己的数字
[抄题]: A self-dividing number is a number that is divisible by every digit it contains. For example, ...