定义:

获取感兴趣的区域(Area Of Interest)的算法。主要用于常用的游戏服务器压力,降低网络风暴的可能。

常见的AOI算法有九宫格,四叉树,灯塔,十字链表等算法。本文主要举例九宫格和四叉树两种算法的golang版本实现。

九宫格

九宫格可以说是最容易理解的一种AOI兴趣算法。

举例: 世界坐标是X[20,200],Y[50,230],划分成6×6的网格为:

  • 已知玩家坐标(x,y),该玩家在几号网格?

从图可看出,九宫格是把地图根据x,y轴坐标划分为等比例的一张网格地图,每个格子带有一个id编号,当玩家每次移动后根据玩家坐标将玩家置入到相应的格子中,通过把玩家在内的九个格子 的所有玩家数据取出得到兴趣用户。

数据结构

type Grid struct {GID      int      //格子IDEntities sync.Map //当前格子内的实体
}type GridManger struct {StartX    int // X区域左边界坐标StartY    int // Y区域上边界坐标AreaWidth int // 格子宽度(长=宽)GridCount int // 格子数量grids     map[int]*Gridpool      sync.Pool
}
通过横纵坐标获取对应的格子ID
func (g *GridManger) getGIDByPos(x, y float64) int {gx := (int(x) - g.StartX) / g.gridWidth()gy := (int(y) - g.StartY) / g.gridWidth()return gy*g.GridCount + gx
}
根据格子的gID得到当前周边的九宫格信息
func (g *GridManger) getSurroundGrids(gID int) []*Grid {grids := g.pool.Get().([]*Grid)defer func() {grids = grids[:0]g.pool.Put(grids)}()if _, ok := g.grids[gID]; !ok {return grids}grids = append(grids, g.grids[gID])// 根据gID, 得到格子所在的坐标x, y := gID%g.GridCount, gID/g.GridCountfor i := 0; i < 8; i++ {newX := x + dx[i]newY := y + dy[i]if newX >= 0 && newX < g.GridCount && newY >= 0 && newY < g.GridCount {grids = append(grids, g.grids[newY*g.GridCount+newX])}}return grids
}

增删查方法

func (g *GridManger) Add(x, y float64, key string) {entity := entityPool.Get().(*Entity)entity.X = xentity.Y = yentity.Key = keyID := g.getGIDByPos(x, y)grid := g.grids[ID]grid.Entities.Store(key, entity)
}func (g *GridManger) Delete(x, y float64, key string) {ID := g.getGIDByPos(x, y)grid := g.grids[ID]if entity, ok := grid.Entities.Load(key); ok {grid.Entities.Delete(key)entityPool.Put(entity)}
}func (g *GridManger) Search(x, y float64) []string {result := resultPool.Get().([]string)defer func() {result = result[:0]resultPool.Put(result)}()ID := g.getGIDByPos(x, y)grids := g.getSurroundGrids(ID)for _, grid := range grids {grid.Entities.Range(func(_, value interface{}) bool {result = append(result, value.(*Entity).Key)return true})}return result
}

四叉树

可以明显看到九宫格算法需要一次性开辟出所有的网格,无论格子中是否存在一定数量的玩家。当一次性出现陈千上万的网格,对服务端的资源浪费可想而知。类似的算法与灯塔算法亦是如此。当然也有一些算法对此做了优化但终有取舍。

四叉树算是一种比较完备的AOI算法。四叉树也是在二维图片中定位像素唯一适合的算法。

数据结构

type Node struct {Leaf      bool      // 是否为叶子节点Deep      int       // 深度AreaWidth float64   // 格子宽度(长=宽)XStart    float64   // 起始范围YStart    float64   // 起始范围Tree      *QuadTree // 树指针Child     [4]*Node  // 子节点Entities  *sync.Map // 实体
}type QuadTree struct {maxCap, maxDeep intradius          float64mPool           sync.Pool*Node
}
检查节点是否可以分割
func (n *Node) canCut() bool {if n.XStart+n.AreaWidth/2 > 0 && n.YStart+n.AreaWidth/2 > 0 {return true}return false
}
检查节点是否需要分割
func (n *Node) needCut() bool {lens := 0n.Entities.Range(func(key, value interface{}) bool {lens++return true})return lens+1 > n.Tree.maxCap && n.Deep+1 <= n.Tree.maxDeep && n.canCut()
}
分割节点
func (n *Node) cutNode() {n.Leaf = falsehalf := n.AreaWidth / 2n.Child[leftUp] = NewSonNode(n.XStart, n.YStart, n)n.Child[rightUp] = NewSonNode(n.XStart+half, n.YStart, n)n.Child[leftDown] = NewSonNode(n.XStart, n.YStart+half, n)n.Child[rightDown] = NewSonNode(n.XStart+half, n.YStart+half, n)// 将实体迁移到对应子节点n.Entities.Range(func(k, v interface{}) bool {entity := v.(*Entity)for _, node := range n.Child {if node.intersects(entity.X, entity.Y) {node.Entities.Store(entity.Key, entity)}}n.Entities.Delete(k)return true})n.Tree.mPool.Put(n.Entities)n.Entities = nil
}

增删查

func (n *Node) Add(x, y float64, name string) {// 判断是否需要分割if n.Leaf && n.needCut() {n.cutNode()}// 非叶子节点往下递归if !n.Leaf {n.Child[n.findSonQuadrant(x, y)].Add(x, y, name)return}entity := entityPool.Get().(*Entity)entity.X = xentity.Y = yentity.Key = name// 叶子节点进行存储n.Entities.Store(entity.Key, entity)
}func (n *Node) Delete(x, y float64, name string) {if !n.Leaf {n.Child[n.findSonQuadrant(x, y)].Delete(x, y, name)return}if entity, ok := n.Entities.Load(name); ok {n.Entities.Delete(name)entityPool.Put(entity)}
}func (n *Node) Search(x, y float64) []string {result := resultPool.Get().([]string)defer func() {result = result[:0]resultPool.Put(result)}()n.search(x, y, &result)return result
}func (n *Node) search(x, y float64, result *[]string) {if !n.Leaf {minX, maxX := x-n.Tree.radius, x+n.Tree.radiusminY, maxY := y-n.Tree.radius, y+n.Tree.radiusfor _, son := range n.Child {if son.intersects(minX, minY) || son.intersects(maxX, minY) ||son.intersects(minX, maxY) || son.intersects(maxX, maxY) {son.search(x, y, result)}}return}n.Entities.Range(func(key, value interface{}) bool {*result = append(*result, value.(*Entity).Key)return true})return
}

总结

四叉树的优势相比九宫格应该有两点

1.当玩家数量比较少的时候,节省了节点的分配的内存

2.当玩家数量比较多的时候能保持每个节点内的玩家数量均衡 但当玩家数量比较多的时候,整个树的内存体积和九宫格体积应该是差不多的,因为一样要存那么多个玩家数据进去

另外四叉树和九宫格都能通过控制视野半径去防止网络风暴

完整代码实例:

https://github.com/knight0zh/aoi

游戏服务器AOI兴趣点算法原理--四叉树与九宫格 (golang)相关推荐

  1. MMORPG游戏中AOI视野算法解析

    转载:https://blog.csdn.net/ybn6775/article/details/81701167 AOI(Area Of Interest),中文就是感兴趣区域.通俗一点说,感兴趣区 ...

  2. 游戏服务器设计模式及算法

    游戏循环 游戏循环是一种典型的游戏编程范式,在游戏之外的领域很少用到. 客户端游戏循环 首先来看客户端的游戏循环,伪代码如下: while (true) {processInput() // 处理(用 ...

  3. 算法:游戏内AOI视野算法(十字链表)

    为什么要有这个算法? 对于一个游戏场景内的实体来说,当战斗频繁的时候,可能存在上千实体的数据同步,例如上千实体移动时的坐标同步,大型战斗场景时全场景实体的属性的同步等等,这样就会造成一个问题,同步数据 ...

  4. 云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第7篇...

    空血来战玩法 & 游戏规则篇 空血来战使用纯正的四川麻将玩法,分为三人模式和四人两种模式,三人模式<三人两房>只用条子和筒子两种花色共72张牌,游戏节奏更快,对战更激烈.四人模式& ...

  5. 「游戏」游戏服务器中AOI的原理及四叉树实现

    前言 要不是想起来这篇文章想写一个关于游戏服务器开发过程中关于AOI相关的文章,我都差不点忘了我是一个游戏服务器开发人员

  6. 开放世界游戏-AOI各算法优劣比较

    在AOI(Area of Interest)算法中,十字链表.四叉树和九宫格都是常用的空间索引结构,用于优化游戏中大量物体的碰撞检测和更新.以下是它们的优缺点: 十字链表 优点: 内存占用小,只需要为 ...

  7. Netty构建游戏服务器(一)--基本概念与原理

    一,Netty是什么 1,Netty是由 JBOSS 提供的一个 java开源 框架. 2,Netty是JAR包,一般使用ALL-IN-ONE的JAR包就可以开发了. 3,Netty不需要运行在Tom ...

  8. 服务器Raid数据恢复成功案例和raid 5数据恢复算法原理

    服务器Raid 5数据恢复案例 本次分享的案例是一台服务器中的raid磁盘阵列,磁盘阵列中有12块磁盘,单盘容量500G,ext3文件系统,系统平台为Linux平台.Raid中2号盘和6号盘两块硬盘报 ...

  9. 游戏光枪坐标定位原理及算法

    http://b2b.hc360.com/supplyself/347075653.html 品牌: CRZMAN 型号: WII 适用送礼场合: 广告促销,商务馈赠,节日,乔迁,生日,展销会,开业典 ...

最新文章

  1. 枚举(Enumerations)
  2. 【洛谷P2023】维护序列
  3. 2020十大新消费人群
  4. AI 应届博士生年薪八十万,贵吗?
  5. 【学术分享】写论文必须养成的十大良好写作习惯
  6. 人工鱼群算法Matlab实现
  7. zlog日志使用说明
  8. python光标图片获取
  9. 苹果logo_“林芝苹果”LOGO图形标识公开评选
  10. 短信验证码mysql_短信验证码
  11. day14课后总结app
  12. NIO与JVM基本概念
  13. 卷积与反卷积(转置卷积)关系的公式推导 及其各自的形式
  14. FusionCharts简单教程(一)---建立第一个FusionCharts图形
  15. 信号与系统——信号的分解
  16. 如何从零开始用PyTorch实现Chatbot?(附完整代码)
  17. Dota2世界冠军OG被AI碾压,全程人类只推掉两座外塔 | 广东省智能创新协会
  18. 携职教育:2022年初级会计考试证书领取流程及所需材料
  19. Unity DOTS学习导航
  20. 老夫金钟罩铁布衫纵横江湖数十年

热门文章

  1. 双三次B样条曲面及其微分几何基本量计算
  2. Sourceforge下载技巧
  3. word段落居中的快捷键_word文字居中的快捷键是什么,word文档文字居中的快捷键是什么?...
  4. 互动投影应用哪些技术与设备
  5. IDA安装keypatch插件
  6. VCS门级仿真系列文章之sdf文件和$sdf_annotate反标
  7. 如何使用CPU导热膏?CPU散热膏正确涂抹使用方法
  8. 施工员培训建筑八大员培训施工员房屋建筑工程现场施工管理剖析
  9. Android平板电脑应用开发的技巧
  10. 速码云信息-----短信接口代码参数