<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
優化的思路一般是: 第一個是儘量降低向用戶端同步物件的數量,第二個是儘量降低單個物件向用戶端同步的資料.
"九宮格"是最常見的視野管理演演算法了.它的優點在於原理和實現都非常簡單.
// 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 < numXSlots; i++ { m.towers[i] = make([]tower, numYSlots) for j := 0; j < numYSlots; j++ { key := NewKey(int64(i), int64(j)) m.towers[i][j].init(int64(key)) } } }
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() }
我們在回過頭來繼續說 mgr 的方法.
前提:
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 <= ximax; xi++ { for yi := yimin; yi <= yimax; yi++ { tower := &m.towers[xi][yi] f(tower) } } } func (aoiman *TowerAOIManager) getWatchedTowers(x, y float64, aoiDistance float64) (int, int, int, int) { ximin, yimin := aoiman.transXY(x-aoiDistance, y-aoiDistance) ximax, yimax := aoiman.transXY(x+aoiDistance, y+aoiDistance) return ximin, ximax, yimin, yimax } func (m *TowerAOIManager) transXY(x, y float64) (int, int) { xi := int((x - m.minX) / m.towerRange) yi := int((y - m.minY) / m.towerRange) return m.normalizeXi(xi), m.normalizeYi(yi) } func (m *TowerAOIManager) normalizeXi(xi int) int { if xi < 0 { xi = 0 } else if xi >= m.xTowerNum { xi = m.xTowerNum - 1 } return xi } func (m *TowerAOIManager) normalizeYi(yi int) int { if yi < 0 { yi = 0 } else if yi >= m.yTowerNum { yi = m.yTowerNum - 1 } return yi }
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 }
每幀移動座標點 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 <= oximax; xi++ { for yi := oyimin; yi <= oyimax; yi++ { if xi >= ximin && xi <= ximax && yi >= yimin && yi <= yimax { continue } tower := &m.towers[xi][yi] tower.removeWatcher(obj) } } for xi := ximin; xi <= ximax; xi++ { for yi := yimin; yi <= yimax; yi++ { if xi >= oximin && xi <= oximax && yi >= oyimin && yi <= oyimax { continue } tower := &m.towers[xi][yi] tower.addWatcher(obj, true) } } return true }
每影格同步化所有區域變化的物體物件
func (m *TowerAOIManager) OnSync() { for i := 0; i < m.xTowerNum; i++ { for j := 0; j < m.yTowerNum; j++ { m.towers[i][j].Broadcast() } } }
簡單的實現了 AOI 區域變化管理,當然後面還需要優化,我們知道"九宮格" 演演算法的缺點:
1 . 當玩家跨越格子的時候,比如說從A點到B點.瞬間會有新增格子,那其中的物件就會進入視野,與此同時,就會有消失的格子,那其中的物件就要消失視野.這個瞬間就會出現一個流量激增點,它可能會導致使用者端卡頓等問題.
2. 流量浪費.有使用者端不需要的物件被同步過來了.我們知道它是基於格子來管理地圖物件的.那麼就會無法保證九宮區域一定剛好是視野範圍.肯定是大於視野區域這樣才保證同步物件正確.(如果是俯視角那種 ,視野就會是一個 梯形範圍.)
或者你可以在伺服器端中,根據使用者端梯形視野在作一遍初篩.
到此這篇關於Go語言實現AOI區域視野管理流程詳解的文章就介紹到這了,更多相關Go AOI區域視野管理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45