首先咱们来讨论下游戏开发中的几个坐标系,为了方便解释,我截取了灯塔AOI DEMO当NPC数目为0时候的样子(代码地址以为有帮助的童鞋记得给我代码点个星^_^)git
先对这张图简单说明下:github
这个是客户端图形实际显示要用到的坐标(和游戏逻辑无关),每一个图片长宽各多少像素,描点在什么位置,可是在游戏中直接使用像素做为计量单位(坐标点)十分不方便,好比物体的移动,何况移动也不会一个像素一个像素移动,因此咱们作了一层抽象(格子),游戏相关的贴图(地图/人物/道具)必须是格子的整数倍,这样游戏中全部的物体才能被正常显示,而不会产生误差。code
什么是格子?格子是游戏逻辑使用的最小单位,表明游戏坐标系的点,它的单位为像素(DEMO中15*15像素为一格),游戏中全部设定都必须是格子的整数倍,因此不会出现有物体在两个格子之间,致使坐标不肯定的状况,物体的移动的步长单位也变成多少格,而不是像素了,即若是使用像素做为坐标点,从(0,0)移动到(0,1),则移动了1个像素,如今用格子做为坐标点,从(0,0)移动到(0,1),则移动了15个像素。对象
什么是瓦片?一张大的世界地图或者背景图能够由几种地形来表示,每种地形对应一张小的的图片,咱们称这些小的地形图片为瓦片。把这些瓦片拼接在一块儿,一个完整的地图就组合出来了,这就是瓦片地图。blog
在DEMO中,为了简化逻辑,我将瓦片设置为一倍格子大小,玩家和NPC的大小也设置为一倍格子大小。游戏
为何须要灯塔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))
对象进入(角色登入、生成怪物):
对象离开(角色登出、怪物被杀死):
对象移动