一文详解Go实现AOI区域视野管理
今天golang学习网给大家带来了《一文详解Go实现AOI区域视野管理》,其中涉及到的知识点包括go语言等等,无论你是小白还是老手,都适合看一看哦~有好的建议也欢迎大家在评论留言,若是看完有所收获,也希望大家能多多点赞支持呀!一起加油学习~
这篇文章主要介绍“一文详解Go实现AOI区域视野管理”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“一文详解Go实现AOI区域视野管理”文章能帮助大家解决问题。
优化的思路一般是: 第一个是尽量降低向客户端同步对象的数量,第二个是尽量降低单个对象向客户端同步的数据.
"九宫格"是最常见的视野管理算法了.它的优点在于原理和实现都非常简单.
// AOI 管理器
type AOIManager interface {
GetWidth() int
GetHeight() int
OnEnter(obj scene.GameObject, enterPos *geom.Vector2d) bool
OnLeave(obj scene.GameObject) bool
OnMove(obj scene.GameObject, movePos *geom.Vector2d) bool
OnSync()
}一.定义管理器接口
1. 进入区域
2. 离开区域
3. 在区域移动
4. 同步信息
具体实现:
type TowerAOIManager struct {
minX, maxX, minY, maxY float64 // 单位 m
towerRange float64 // 格子大小
towers [][]tower
xTowerNum, yTowerNum int
}划分格子: 按照实际情况出发,规定格子大小 towerRange. (一般 九个格子的范围需大于屏幕看到的视野范围) 这样才能保证客户端场景物体的生成和消失在玩家屏幕外.不会突然出现.
// 构造结构
func NewTowerAOIManager(minX, maxX, minY, maxY float64, towerRange float64) AOIManager {
mgr := &TowerAOIManager{minX: minX, maxX: maxX, minY: minY, maxY: maxY, towerRange: towerRange}
mgr.init()
return mgr
}
func (m *TowerAOIManager) init() {
numXSlots := int((m.maxX-m.minX)/m.towerRange) + 1
m.xTowerNum = numXSlots
numYSlots := int((m.maxY-m.minY)/m.towerRange) + 1
m.yTowerNum = numYSlots
m.towers = make([][]tower, numXSlots)
for i := 0; i 二.定义区域tower
type tower struct {
towerId int64
context *TowerSyncContext
mapId2Obj map[uint32]scene.GameObject // obj容器
mapId2Watcher map[uint32]scene.GameObject // 观察集合
}func (t *tower) init(key int64) {
t.towerId = key
t.context = NewTowerSyncContext() // 同步信息
t.mapId2Obj = make(map[uint32]scene.GameObject)
t.mapId2Watcher = make(map[uint32]scene.GameObject)
}func (t *tower) AddObj(obj scene.GameObject, fromOtherTower scene.AOITower, bExclude bool) {
obj.SetAOITower(t)
t.mapId2Obj[obj.GetId()] = obj
if fromOtherTower == nil {
for watcherId, watcher := range t.mapId2Watcher {
if bExclude && watcherId == obj.GetId() {
continue
}
watcher.OnEnterAOI(obj)
}
} else {
// obj moved from other tower to this tower
for watcherId, watcher := range fromOtherTower.GetWatchers() {
if watcherId == obj.GetId() {
continue
}
if _, ok := t.mapId2Watcher[watcherId]; ok {
continue
}
watcher.OnLeaveAOI(obj)
}
for watcherId, watcher := range t.mapId2Watcher {
if watcherId == obj.GetId() {
continue
}
if _, ok := fromOtherTower.GetWatchers()[watcherId]; ok {
continue
}
watcher.OnEnterAOI(obj)
}
}
}
func (t *tower) RemoveObj(obj scene.GameObject, notifyWatchers bool) {
obj.SetAOITower(nil)
delete(t.mapId2Obj, obj.GetId())
if notifyWatchers {
for watcherId, watcher := range t.mapId2Watcher {
if watcherId == obj.GetId() {
continue
}
watcher.OnLeaveAOI(obj)
}
}
}
func (t *tower) addWatcher(obj scene.GameObject, bExclude bool) {
if bExclude {
if _, ok := t.mapId2Watcher[obj.GetId()]; ok {
// todo log
return
}
}
t.mapId2Watcher[obj.GetId()] = obj
// now obj can see all objs under this tower
for neighborId, neighbor := range t.mapId2Obj {
if neighborId == obj.GetId() {
continue
}
obj.OnEnterAOI(neighbor)
}
}
func (t *tower) removeWatcher(obj scene.GameObject) {
if _, ok := t.mapId2Watcher[obj.GetId()]; !ok {
// todo log
return
}
delete(t.mapId2Watcher, obj.GetId())
for neighborId, neighbor := range t.mapId2Obj {
if neighborId == obj.GetId() {
continue
}
obj.OnLeaveAOI(neighbor)
}
}
func (t *tower) GetWatchers() map[uint32]scene.GameObject {
return t.mapId2Watcher
}
func (t *tower) GetObjs() map[uint32]scene.GameObject {
return t.mapId2Obj
}
func (t *tower) GetTowerId() int64 {
return t.towerId
}
func (t *tower) AddSyncData(mod uint16, cmd uint16, msg protoreflect.ProtoMessage) {
t.context.AddSyncData(mod, cmd, msg)
}
func (t *tower) Broadcast() {
if len(t.context.fights) == 0 {
return
}
// 广播协议
....
t.context.ClearContext()
}三.AOI的具体方法实现
我们在回过头来继续说 mgr 的方法.
1.进入实现
前提:
GameObject : 一切场景物体的基础接口
type GameObject interface {}
Vector2d : X,Y 坐标
type Vector2d struct {
x, y, w float64
}具体实现:
如果是从上一个区域内离开,则先走 离开上一个区域,然后计算当前进入位置坐标对应的九宫区域,
然后把obj 加入到各个区域内
func (m *TowerAOIManager) OnEnter(obj scene.GameObject, enterPos *geom.Vector2d) bool {
if obj.GetAOITower() != nil {
m.OnLeave(obj) // 离开上一个区域
}
obj.SetPosition(enterPos) // 设置当前位置
// obj 视野范围内的所有区域
m.visitWatchedTowers(enterPos, obj.GetViewRange(), func(tower *tower) {
tower.addWatcher(obj, false)
})
t := m.getTowerXY(enterPos)
// 当前位置所在的区域
t.AddObj(obj, nil, false)
return true
}
func (m *TowerAOIManager) getTowerXY(xyPos *geom.Vector2d) *tower {
xi, yi := m.transXY(xyPos.GetX(), xyPos.GetY())
return &m.towers[xi][yi]
}关键的方法:
计算obj当前位置中,视野内能被观察到的所有区域.
func (m *TowerAOIManager) visitWatchedTowers(xyPos *geom.Vector2d, aoiDistance float64, f func(*tower)) {
ximin, ximax, yimin, yimax := m.getWatchedTowers(xyPos.GetX(), xyPos.GetY(), aoiDistance)
for xi := ximin; xi = m.xTowerNum {
xi = m.xTowerNum - 1
}
return xi
}
func (m *TowerAOIManager) normalizeYi(yi int) int {
if yi = m.yTowerNum {
yi = m.yTowerNum - 1
}
return yi
}2.离开区域
func (m *TowerAOIManager) OnLeave(obj scene.GameObject) bool {
obj.GetAOITower().RemoveObj(obj, true) // 离开当前区域
// 查找视野内所有区域,然后从关注列表中移除
m.visitWatchedTowers(obj.GetPosition(), obj.GetViewRange(), func(tower *tower) {
tower.removeWatcher(obj)
})
return true
}3.移动
每帧移动坐标点 movePos
func (m *TowerAOIManager) OnMove(obj scene.GameObject, movePos *geom.Vector2d) bool {
oldX, oldY := obj.GetPosition().GetX(), obj.GetPosition().GetY()
obj.SetPosition(movePos) //设置当前坐标
t0 := obj.GetAOITower()
t1 := m.getTowerXY(movePos)
// 判断移动是否跨区域了
if t0.GetTowerId() != t1.GetTowerId() {
t0.RemoveObj(obj, false)
t1.AddObj(obj, t0, true)
}
// 计算前后变化的区域,进行移除和添加关注列表
oximin, oximax, oyimin, oyimax := m.getWatchedTowers(oldX, oldY, obj.GetViewRange())
ximin, ximax, yimin, yimax := m.getWatchedTowers(movePos.GetX(), movePos.GetY(), obj.GetViewRange())
for xi := oximin; xi = ximin && xi = yimin && yi = oximin && xi = oyimin && yi 4.同步
每帧同步所有区域变化的物体对象
func (m *TowerAOIManager) OnSync() {
for i := 0; i 简单的实现了 AOI 区域变化管理,当然后面还需要优化,我们知道"九宫格" 算法的缺点:
1 . 当玩家跨越格子的时候,比如说从A点到B点.瞬间会有新增格子,那其中的对象就会进入视野,与此同时,就会有消失的格子,那其中的对象就要消失视野.这个瞬间就会出现一个流量激增点,它可能会导致客户端卡顿等问题.
2. 流量浪费.有客户端不需要的对象被同步过来了.我们知道它是基于格子来管理地图对象的.那么就会无法保证九宫区域一定刚好是视野范围.肯定是大于视野区域这样才保证同步对象正确.(如果是俯视角那种 ,视野就会是一个 梯形范围.)

或者你可以在服务端中,根据客户端梯形视野在作一遍初筛.
关于“一文详解Go实现AOI区域视野管理”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注golang学习网行业资讯频道,小编每天都会为大家更新不同的知识点。
好了,本文到此结束,带大家了解了《一文详解Go实现AOI区域视野管理》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!
实例详解GO的回调函数怎么用
- 上一篇
- 实例详解GO的回调函数怎么用
- 下一篇
- 详解关于Redis的内存碎片清理
-
- Golang · Go教程 | 1天前 | map · 并发安全 · RWMutex · sync.Map · Go教程 · 并发安全 RWMutex sync.Map Go map并发读写 go test race
- Go map 并发读写崩溃怎么办:从复现报错到 RWMutex 修复的完整流程
- 272浏览 收藏
-
- Golang · Go教程 | 3天前 | singleflight · 并发控制 · Go教程 · 缓存治理 · 接口优化 · Go 并发请求 缓存击穿 singleflight 缓存回填
- Go singleflight 防缓存击穿实战:相同请求只查一次数据库
- 114浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 213次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 235次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 205次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 370次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 369次使用
-
- 有关Go语言拼接URL路径的方法
- 2023-03-09 185浏览
-
- go语言能不能做后端
- 2023-03-03 460浏览
-
- go语言和java的区别是什么
- 2023-03-03 430浏览
-
- go语言如何进行强制类型转换
- 2023-03-04 450浏览
-
- go语言的beego怎么使用
- 2023-03-03 320浏览

