AOI主要有九宫格、灯塔和十字链表的算法实现。本文阐述十字链表的实现和尝试。

1. 基本原理

根据二维地图,将其分成x轴和y轴两个链表。如果是三维地图,则还需要维护多一个z轴的链表。将对象的坐标值按照大小相应的排列在相应的坐标轴上面。

2. 基本接口

对对象的操作主要有以下三个接口:

add:对象进入地图;

leave:对象离开地图;

move:对象在地图内移动。

2. 算法实现

既然是链表,很自然地想到用线性表来实现。因为存在向前和向后找的情况,所以使用双链表实现。其实实现也是非常简单,就是两个双链表(这里以二维地图举例)。那么我们的节点需要四个指针,分布为x轴的前后指针,y轴的前后指针。

//双链表(对象)class DoubleNode

{

public:

DoubleNode(string key, int x, int y)

{

this->key = key;

this->x = x;

this->y = y;

xPrev = xNext = NULL;

};

DoubleNode * xPrev;

DoubleNode * xNext;

DoubleNode * yPrev;

DoubleNode * yNext;

string key;  //只是一个关键字    int x; //位置(x坐标)    int y; //位置(y坐标)

private:

};

下面是地图场景信息和接口。这里的实现比较粗略,是带头尾的的双链表,暂时不考虑空间占用的问题。类Scene有分别有一个头尾指针,初始化的时候会为其赋值,主要用DoubleNode类的指针来存储x轴和y轴的头尾。初始化的时候,将_head的next指针指向尾_tail;将_tail的prev指针指向_head。

//地图/场景class Scene

{

public:

Scene()

{

this->_head = new DoubleNode("[head]", 0, 0); //带头尾的双链表(可优化去掉头尾)        this->_tail = new DoubleNode("[tail]", 0, 0);

_head->xNext = _tail;

_head->yNext = _tail;

_tail->xPrev = _head;

_tail->yPrev = _head;

};

//对象加入(新增)    DoubleNode * Add(string name, int x, int y);

//对象离开(删除)    void Leave(DoubleNode * node);

//对象移动    void Move(DoubleNode * node, int x, int y);

//获取范围内的AOI (参数为查找范围)    void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen);

private:

DoubleNode * _head;

DoubleNode * _tail;

};

2.1. add(进入地图)

将DoubleNode对象插入到十字链表中。x轴和y轴分别处理,处理方法基本一致。其实就是双链表的数据插入操作,需要从头开始遍历线性表,对比相应轴上的值的大小,插入到合适的位置。

void _add(DoubleNode * node)

{

//x轴处理    DoubleNode * cur = _head->xNext;

while(cur != NULL)

{

if((cur->x > node->x) || cur==_tail) //插入数据        {

node->xNext = cur;

node->xPrev = cur->xPrev;

cur->xPrev->xNext = node;

cur->xPrev = node;

break;

}

cur = cur->xNext;

}

//y轴处理    cur = _head->yNext;

while(cur != NULL)

{

if((cur->y > node->y) || cur==_tail) //插入数据        {

node->yNext = cur;

node->yPrev = cur->yPrev;

cur->yPrev->yNext = node;

cur->yPrev = node;

break;

}

cur = cur->yNext;

}

}

假设可视范围为x轴2以内,y轴2以内,则运行:

分别插入以下数据a(1,5)、f(6,6)、c(3,1)、b(2,2)、e(5,3),然后插入d(3,3),按照x轴和y轴打印其双链表结果;

插入d(3,3)数据,求其可视AOI范围(如图,除了f(6,6),其它对象都在d的可视范围内)。

控制台结果(前8行):

步骤1结果图示:

步骤2结果图示:

2.2. leave(离开地图)和move(移动)

其实都是双链表的基本操作,断掉其相应的指针就好了。按理,是需要

move和leave操作如图,move是将d(3,3)移动到(4,4),然后再打印其AOI范围。

控制台结果:

移动后AOI范围图示:

3. 完整代码实例

//ConsoleApplication2.cpp : 定义控制台应用程序的入口点。//

#include "stdafx.h"

#include "stdafx.h"

#include "stdio.h"

#include

#include

using namespace std;

//双链表(对象)class DoubleNode

{

public:

DoubleNode(string key, int x, int y)

{

this->key = key;

this->x = x;

this->y = y;

xPrev = xNext = NULL;

};

DoubleNode * xPrev;

DoubleNode * xNext;

DoubleNode * yPrev;

DoubleNode * yNext;

string key;

int x; //位置(x坐标)    int y; //位置(y坐标)

private:

};

//地图/场景class Scene

{

public:

Scene()

{

this->_head = new DoubleNode("[head]", 0, 0); //带头尾的双链表(可优化去掉头尾)        this->_tail = new DoubleNode("[tail]", 0, 0);

_head->xNext = _tail;

_head->yNext = _tail;

_tail->xPrev = _head;

_tail->yPrev = _head;

};

//对象加入(新增)    DoubleNode * Add(string name, int x, int y)

{

DoubleNode * node = new DoubleNode(name, x, y);

_add(node);

return node;

};

//对象离开(删除)    void Leave(DoubleNode * node)

{

node->xPrev->xNext = node->xNext;

node->xNext->xPrev = node->xPrev;

node->yPrev->yNext = node->yNext;

node->yNext->yPrev = node->yPrev;

node->xPrev = NULL;

node->xNext = NULL;

node->yPrev = NULL;

node->yNext = NULL;

};

//对象移动    void Move(DoubleNode * node, int x, int y)

{

Leave(node);

node->x = x;

node->y = y;

_add(node);

};

//获取范围内的AOI (参数为查找范围)    void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen)

{

cout <key <x <y <

cout <

//往后找        DoubleNode * cur = node->xNext;

while (cur != _tail)

{

if ((cur->x - node->x) > xAreaLen)

{

break;

}

else

{

int inteval = 0;

inteval = node->y - cur->y;

if (inteval >= -yAreaLen && inteval <= yAreaLen)

{

cout <key <x <y <

}

}

cur = cur->xNext;

}

//往前找        cur = node->xPrev;

while (cur != _head)

{

if ((node->x - cur->x) > xAreaLen)

{

break;

}

else

{

int inteval = 0;

inteval = node->y - cur->y;

if (inteval >= -yAreaLen && inteval <= yAreaLen)

{

cout <key <x <y <

}

}

cur = cur->xPrev;

}

};

//调试代码    void PrintLink()  //打印链表(从头开始)    {

//打印x轴链表        DoubleNode * cur = _head->xNext;

while (cur != _tail)

{

cout <key) <x) <y) < ";

cur = cur->xNext;

}

cout <

//打印y轴链表        cur = _head->yNext;

while (cur != _tail)

{

cout <key) <x) <y) < ";

cur = cur->yNext;

}

cout <

};

private:

DoubleNode * _head;

DoubleNode * _tail;

void _add(DoubleNode * node)

{

//x轴处理        DoubleNode * cur = _head->xNext;

while (cur != NULL)

{

if ((cur->x > node->x) || cur == _tail) //插入数据            {

node->xNext = cur;

node->xPrev = cur->xPrev;

cur->xPrev->xNext = node;

cur->xPrev = node;

break;

}

cur = cur->xNext;

}

//y轴处理        cur = _head->yNext;

while (cur != NULL)

{

if ((cur->y > node->y) || cur == _tail) //插入数据            {

node->yNext = cur;

node->yPrev = cur->yPrev;

cur->yPrev->yNext = node;

cur->yPrev = node;

break;

}

cur = cur->yNext;

}

}

};

//--------------------------------------------

int _tmain(int argc, _TCHAR* argv[])

{

Scene scene = Scene();

//增加    scene.Add("a", 1, 5);

scene.Add("f", 6, 6);

scene.Add("c", 3, 1);

scene.Add("b", 2, 2);

scene.Add("e", 5, 3);

DoubleNode * node = scene.Add("d", 3, 3);

scene.PrintLink();

scene.PrintAOI(node, 2, 2);

//移动    cout <

scene.Move(node, 4, 4);

scene.PrintLink();

scene.PrintAOI(node, 2, 2);

//删除    cout <

scene.Leave(node);

scene.PrintLink();

}

算法的大概思想如下.每个场景维护两个链表,分别为X轴和Y轴的坐标按序排列好的链表,也就是比如在X轴链表上,越在前的对象,X坐标越小,Y轴链表同理.这样,每次需要更新状态的时候,只需要在这个链表上向前或者向后遍历结点就知道该通知谁了.

这里假设有三个API:Add(向场景中添加一个对象),Leave(某对象离开场景),Move(某对象在场景中移动).

来一个一个看.

Add:根据新增对象的X,Y坐标,依次遍历X,Y轴坐标链表,这里有两个目的,一个是获得这个新增对象的坐标在X,Y轴坐标的位置,另一方面获得该通知哪些结点.通知的范围,每个对象可以自己定制自己的通知范围.但是为了简单起见,在这里我们假设每个结点X,Y坐标相差1,而通知的范围是2.比如原有的X轴坐标为:

a->b->c->d->e->f->g->h

假设新增一个对象z,它最终所在的位置是c和d之间,根据前面的假设,它只需要通知b,c,e,f四个结点就好了.所以,新增一个结点的时候,并不需要遍历完链表的.

但是这里还需要注意的是,一个结点,必须X,Y坐标同时都在通知范围内才可以进入通知集合.

Leave:在添加了对象之后,对象身上挂了两个结点,分别存放在X,Y轴坐标链表上的位置,那么在这个对象要离开场景时,也只需要向前向后探索一定的范围,就可以得到需要通知该对象要离开的集合了.

Move:移动操作比较麻烦,但是也可以比较漂亮的解决.在更新位置之前,同样根据前面的算法得到要通知的结点集合,称为old_set;更新位置之后,再获取另一个通知集合,称为new_set;然后,old_set和new_set的交集,称为move_set,在此集合中的结点,在移动前后都在通知范围,因此要向这个集合的结点发送该对象移动的消息;而old_set-move_set的集合,在移动之后已经离开了视野,因此要向它们发送该对象离开的消息;最后,new_set-move_set是移动之后才看见该结点的结点集合,这个集合的结点,要发送该结点进入场景的消息.

java aoi 服务器地图_aoi--地图视野处理相关推荐

  1. java aoi 服务器地图_GitHub - WanZixin/getShp: 利用高德地图web服务API获取坐标串,生成行政区和aoi的shp文件...

    爬取数据生成shp文件 1.功能简介 共有两大功能,一个功能是根据高德地图web服务API获取行政区划坐标串,写入行政区shp文件:另一个功能是根据高德地图的接口获取poi坐标串,写入aoi(area ...

  2. Android课程设计:基于离线地图服务器的Android地图应用

    Android开发课程设计:基于离线地图服务器的Android地图应用 此项目的灵感来源于伯克利cs61b的Project3: cs61b的官网地址:Project 3: Bear Maps 我的实验 ...

  3. 传奇怎么设置不显示服务器,如何将传奇服务器未知神殿地图修改为不限制进出...

    未知神殿地图想必每一个传奇相关人员都是知道的,如果您还不知道这个地图的存在和作用,那么一为原创建议您暂时放下手里的工作.因为对于一个传奇私服gm来说想要做好一个服务器,了解这个地图可以说是最基本的事情 ...

  4. Java 保姆级教程——3.添加地图,地图根据人物移动

    Java 保姆级教程--3.添加地图,地图根据人物移动 注:经过上期的学习,我们学习了如何创建JFrame窗体.如何添加监听事件.根据大家给我的反馈,这一期给大家讲述:如何添加地图.地图如何根据人物移 ...

  5. 我的世界服务器怎么修改地图,《我的世界手机版》pocketmine服务器怎么更换地图?...

    我的世界手机版的服务器辛苦建造好之后,玩家都想像单机版那样在我的世界的服务器中更换地图,服务器地图应该放在哪?很多玩家都喜欢在服务器中设置一个主城,那么服务器的地图要怎么更换呢?今天当乐网小编给大家带 ...

  6. android java 经纬度(国标WGS84/gps84/硬件/谷歌地球卫星,Gcj02/腾讯地图/高德地图,Bd09/百度地图)之间互换

    android java 经纬度(国标WGS84/gps84/硬件/谷歌地球卫星,Gcj02/腾讯地图/高德地图,Bd09/百度地图)之间互换 前言: 由于项目需要,很多时候都需要地图经纬度直接的转换 ...

  7. C# .NET winform地图开发GMap离线地图在线地图自建地图服务器

    目录 添加GMap引用 下载GMap demo 尝试一个测试程序 下载离线地图 使用离线模式读取地图 加载高德地图 获取WMTS 坐标系转化 GMap常用地图操作 自建地图服务器 添加GMap引用 下 ...

  8. 我的世界linux服务器怎么换地图,《我的世界手机版》pocketmine服务器怎么更换地图?...

    原标题:<我的世界手机版>pocketmine服务器怎么更换地图? 我的世界手机版的服务器辛苦建造好之后,玩家都想像单机版那样在我的世界的服务器中更换地图,服务器地图应该放在哪?很多玩家都 ...

  9. 神奇宝贝java路径_【NDS地图制作教程零一】如何打开工具——JAVA第一课

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 说明:Pokemon DS Map Studio是用于为口袋妖怪NDS游戏创建地图的工具,以下简称PDSMS.必须将此工具与SDSME一起使用,以将制作好 ...

最新文章

  1. STM32的RTC简单操作
  2. python pil png合成gif储存时变黑_使用 Python 玩转图片
  3. Android实例-ImageList与Image的应用
  4. php 连续点击事件,javascript设置连续两次点击按钮时间间隔的方法_javascript技巧...
  5. golang插入字符串_golang 几种字符串的连接方式
  6. html transform属性,css3 transform属性详解
  7. wordpress通过$wpdp更新数据表内容
  8. 【论文翻译】CenterNet: Objects as Points
  9. web3.eth.estimateGas
  10. iterm2 配置安装rz sz
  11. JAVA 三个框架结合运用思路_SSM框架的整合与使用——实现简单的转账系统
  12. 浏览器书签有效性验证
  13. matlab绘制正弦曲线
  14. Cookie和Session的作用,区别和各自的应用范围,cookie、Session工作原理
  15. MSDC 4.3 接口规范(18)
  16. ThinkPHP 微信支付及退款
  17. 人人公司启动史上最大规模校园招聘
  18. 【echarts】在柱状图上方显示数值
  19. 怎样看待Android的发展前景?以及Android开发的职业规划
  20. [.net 面向对象编程基础] (20) LINQ使用

热门文章

  1. xftp6设置默认打开文件的程序_xftp6如何使用,教你几个步骤了解xftp6如何使用
  2. Java集合 - ConcurrentHashMap
  3. java基础系列(四)
  4. easyexcel按照模板导出数据
  5. 前端JS如何获取日历
  6. 2018服务器 芯片组,【2018年核心平台年终盘点 】多核心处理器大普及,显卡体验/消费双升级...
  7. “多个帮主同意某人加入工会”思考异步编程的问题
  8. 解决关于win7共享文件连接数限制的问题
  9. 怀旧服10月3日服务器维护,剑网3缘起:怀旧服世界频道惨遭维护,这些段子手忍不住了?...
  10. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java水果库存管理系统30q2h