基于格子的AOI算法

AOI(Area Of Interest )是网游服务器中必不可少要考虑的一个问题,当前主流的AOI算法依然是
基于2D的格子的, 场景均分成为等大的格子,每个Entity都根据其位置放到所属格子的链表中.

关键问题是当Entity移动后:
一是要找到该Entity看到了哪些新的otherEntitys,
二是要找到哪些otherEntitys以前可以看到而现在看不到了,
三是要找到该Entity被哪些新的otherEntitys看到了,
四是要找出以前看得到自己现在看不到自己的otherEntitys.


1, 假定所有Entity的看到见的格子的数量都一样,为N*N,这意味着自己看得见的Entity也一定看得见自己,而
自己看不见的Entity也一定看不见自己.因此当Entity移动后:

<1>,找出Entity移动后新增的可见格子,将这些格子中的其它Entity加入自己的可见列表中(或者通知客户端有新的Entity可见).
<2>,找出Entity移动前可见而移动后不可见的格子,将这些格子中的其它Entity从自己的可见列表中移除(或者通知客户端这些Entity不可见).
<3>,找出Entity移动后新增的可见格子,将自已加入到这些格子中的其它Entity的可见列表中(或者通知其对应的客户端有新的Entity可见).
<4>,找出Entity移动前可见而移动后不可见的格子,将自己从这些格子中的其它Entity的可见列表中移除(或者通知其对应的客户端本Entity不可见).

2, 如果每个Entity的视野大小不等。每个格子增加一个observerGrids链表,保存所有"存在看得见该格子的对象"的格子。
当Entity移动后:
假定Entity移动前所在的gird命名为oldGrid, 移动后所在的grid命名为newGrid.

<1>,找到该Entity看到了哪些新的otherEntitys. 
 
    找出Entity移动后新增的可见格子, 记为newVisibleGrids.    
    算法如下:

    foreach nGrid in newVisibleGrids
    {
         foreach nEntity in nGrid.entityList
         {
             将nEntity加入到thisEntity的可见列表中.(通知thisEntity对应的客户端nEntity可见).
         }
         将newGrid加入nGrid.observerGirds中.
    }

<2>,找到哪些otherEntitys以前可以看到而现在看不到了.
   
    找出Entity移动前可见而移动后不可见的格子,记为newHideGrids.
    算法如下:

    foreach nhGrid in newHideGrids
    {
       //找出以前看得到而移动后看不到的entity.
       for nhEntity in nhGrid.entityList
       {
          把nhEntity从thisEntity的可见列表中移除(通知客户端nhEntity不可见).
       } 
       如果oldGrid.entityList中的所有entity都看不见nhGrid,则将oldGrid从nhGrid.observerGrids中移除.      
    }
    注:可以考虑在每个grid中记下所属entity的最大视野,最小视野,平均视野优化移除算法.
         
<3>,找到该Entity被哪些新的otherEntitys看到了. 

        foreach nGrid in newGrid.observerGrids
        { 
               //优化:这里可以先排除掉同时存在于oldGrid.observerGrids中的nGrid?
               foreach otherEntity in nGrid.entityList
               {
                   oldDist = otherEntity.pos - thisEntity.oldPos; //oldPos为entity移动前的位置.
                   newDist = otherEntity.pos - thisEntity.pos;
                   //检测对于otherEntity来说是否为新增的可见entity.
                   如果oldDist > otherEntity.viewDist 且 newDist <= otherEntity.viewDist
                      将thisEntity加入到otherEntity的可见列表中(通知otherEntity对应的客户端thisEntity可见).
               }          
        }

<4>,找出以前看得到自己现在看不到自己的otherEntitys.
        foreach oGrid in oldGrid.observerGrids
        { 
               //优化:这里可以先排除掉同时存在于newGird.observerGrids中的oGrid?
               foreach otherEntity in oGrid.entityList
               {
                   oldDist = otherEntity.pos - thisEntity.oldPos; //oldPos为entity移动前的位置.
                   newDist = otherEntity.pos - thisEntity.pos;
                   //检测对于otherEntity来说是否为新增的不可见entity.
                   如果oldDist <= otherEntity.viewDist 且 newDist > otherEntity.viewDist
                      将thisEntity从otherEntity的可见列表中移除(通知otherEntity对应的客户端thisEntity不可见).
               }          
        }

### AOI算法在SLG游戏中的实现 #### 定义与作用 AOI(Area Of Interest),即感兴趣的区域,在策略游戏中用于限定玩家仅接收其周围一定范围内发生的事件和状态更新。这种方式能够显著降低网络带宽消耗和服务端处理压力,同时改善用户体验。 对于SLG这类强调宏观布局与微观操作并重的游戏类型来说,合理运用AOI机制尤为重要。通过设定合理的兴趣区域大小以及高效的邻居查找方式,可以在不影响核心玩法的前提下有效提升性能表现[^1]。 #### 实现方案概述 一种常见的做法是在服务端维护一张二维数组形式的地图格子表,其中每个元素代表一个小方块内的实体列表;每当有新的单位进入某个特定的小方块时,则将其加入对应的列表之中。当检测到某位玩家移动位置后,便依据新坐标重新计算其所处的兴趣区间,并向该区域内所有成员发送最新的变动通知。 为了进一步优化查询速度,还可以引入空间索引结构如四叉树或KD-Tree等辅助工具来进行快速定位。不过需要注意的是,这些高级数据结构往往伴随着更高的构建成本及内存占用率,因此具体选用哪种还需视项目规模和技术栈特点而定[^2]。 ```cpp // C++伪代码展示如何利用简单的栅格化方法来追踪玩家附近的动态情况 class GridBasedAOISystem { public: void AddEntity(Entity* entity); void RemoveEntity(Entity* entity); std::vector<Entity*> GetNearbyEntities(const Position& pos); private: int gridSize; // 单元格尺寸参数 std::unordered_map<int, std::list<Entity*>> gridMap; }; void GridBasedAOISystem::AddEntity(Entity* entity){ auto key = CalculateGridKey(entity->GetPosition()); if(gridMap.find(key)==gridMap.end()){ gridMap[key]=std::list<Entity*>(); } gridMap[key].push_back(entity); } std::vector<Entity*> GridBasedAOISystem::GetNearbyEntities(const Position& pos){ std::vector<Entity*> result; for(auto offset : nearbyOffsets){ // 预先定义好的相邻单元偏移量集合 auto targetPos=pos+offset; auto key=CalculateGridKey(targetPos); if(gridMap.find(key)!=gridMap.end()) result.insert(result.end(),gridMap[key].begin(),gridMap[key].end()); } return result; } ``` 此段代码展示了基于固定大小网格划分的方法,它简单易懂且易于扩展支持更多功能特性,非常适合初学者学习理解AOI概念及其工作流程[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值