最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語(yǔ)言實(shí)現(xiàn)AOI區(qū)域視野管理流程詳解

 更新時(shí)間:2023年03月06日 11:27:25   作者:zhizhizhi志  
在游戲中,場(chǎng)景里存在大量的物體.如果我們把所有物體的變化都廣播給玩家.那客戶端很難承受這么大的壓力.因此我們肯定會(huì)做優(yōu)化.把不必要的信息過(guò)濾掉.如只關(guān)心玩家視野所看到的.減輕客戶端的壓力,給玩家更流暢的體驗(yàn)

優(yōu)化的思路一般是: 第一個(gè)是盡量降低向客戶端同步對(duì)象的數(shù)量,第二個(gè)是盡量降低單個(gè)對(duì)象向客戶端同步的數(shù)據(jù).

"九宮格"是最常見(jiàn)的視野管理算法了.它的優(yōu)點(diǎn)在于原理和實(shí)現(xiàn)都非常簡(jiǎn)單.

// 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. 進(jìn)入?yún)^(qū)域

2. 離開(kāi)區(qū)域

3. 在區(qū)域移動(dòng)

4. 同步信息

具體實(shí)現(xiàn):

type TowerAOIManager struct {
	minX, maxX, minY, maxY float64 // 單位 m
	towerRange             float64 // 格子大小
	towers                 [][]tower
	xTowerNum, yTowerNum   int
}

劃分格子: 按照實(shí)際情況出發(fā),規(guī)定格子大小 towerRange. (一般 九個(gè)格子的范圍需大于屏幕看到的視野范圍) 這樣才能保證客戶端場(chǎng)景物體的生成和消失在玩家屏幕外.不會(huì)突然出現(xiàn).

// 構(gòu)造結(jié)構(gòu)
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))
		}
	}
}

二.定義區(qū)域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
	}
	// 廣播協(xié)議
	 ....   
	t.context.ClearContext()
}

三.AOI的具體方法實(shí)現(xiàn)

我們?cè)诨剡^(guò)頭來(lái)繼續(xù)說(shuō) mgr 的方法.

1.進(jìn)入實(shí)現(xiàn)

前提:

GameObject : 一切場(chǎng)景物體的基礎(chǔ)接口

type GameObject interface {}

Vector2d : X,Y 坐標(biāo)

type Vector2d struct {
	x, y, w float64
}

具體實(shí)現(xiàn):

如果是從上一個(gè)區(qū)域內(nèi)離開(kāi),則先走 離開(kāi)上一個(gè)區(qū)域,然后計(jì)算當(dāng)前進(jìn)入位置坐標(biāo)對(duì)應(yīng)的九宮區(qū)域,

然后把obj 加入到各個(gè)區(qū)域內(nèi)

func (m *TowerAOIManager) OnEnter(obj scene.GameObject, enterPos *geom.Vector2d) bool {
	if obj.GetAOITower() != nil {
		m.OnLeave(obj) // 離開(kāi)上一個(gè)區(qū)域
	}
	obj.SetPosition(enterPos) // 設(shè)置當(dāng)前位置
    // obj 視野范圍內(nèi)的所有區(qū)域
	m.visitWatchedTowers(enterPos, obj.GetViewRange(), func(tower *tower) {
		tower.addWatcher(obj, false)
	})
	t := m.getTowerXY(enterPos)
    // 當(dāng)前位置所在的區(qū)域
	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]
}

關(guān)鍵的方法:

計(jì)算obj當(dāng)前位置中,視野內(nèi)能被觀察到的所有區(qū)域.

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
}

2.離開(kāi)區(qū)域

func (m *TowerAOIManager) OnLeave(obj scene.GameObject) bool {
	obj.GetAOITower().RemoveObj(obj, true) // 離開(kāi)當(dāng)前區(qū)域
    // 查找視野內(nèi)所有區(qū)域,然后從關(guān)注列表中移除
	m.visitWatchedTowers(obj.GetPosition(), obj.GetViewRange(), func(tower *tower) {
		tower.removeWatcher(obj)
	})
	return true
}

3.移動(dòng)

每幀移動(dòng)坐標(biāo)點(diǎn) movePos

func (m *TowerAOIManager) OnMove(obj scene.GameObject, movePos *geom.Vector2d) bool {
	oldX, oldY := obj.GetPosition().GetX(), obj.GetPosition().GetY()
	obj.SetPosition(movePos) //設(shè)置當(dāng)前坐標(biāo)
	t0 := obj.GetAOITower()
	t1 := m.getTowerXY(movePos)
    // 判斷移動(dòng)是否跨區(qū)域了
	if t0.GetTowerId() != t1.GetTowerId() {
		t0.RemoveObj(obj, false)
		t1.AddObj(obj, t0, true)
	}
    // 計(jì)算前后變化的區(qū)域,進(jìn)行移除和添加關(guān)注列表
	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
}

4.同步

每幀同步所有區(qū)域變化的物體對(duì)象

func (m *TowerAOIManager) OnSync() {
	for i := 0; i < m.xTowerNum; i++ {
		for j := 0; j < m.yTowerNum; j++ {
			m.towers[i][j].Broadcast()
		}
	}
}

簡(jiǎn)單的實(shí)現(xiàn)了 AOI 區(qū)域變化管理,當(dāng)然后面還需要優(yōu)化,我們知道"九宮格" 算法的缺點(diǎn):

1 . 當(dāng)玩家跨越格子的時(shí)候,比如說(shuō)從A點(diǎn)到B點(diǎn).瞬間會(huì)有新增格子,那其中的對(duì)象就會(huì)進(jìn)入視野,與此同時(shí),就會(huì)有消失的格子,那其中的對(duì)象就要消失視野.這個(gè)瞬間就會(huì)出現(xiàn)一個(gè)流量激增點(diǎn),它可能會(huì)導(dǎo)致客戶端卡頓等問(wèn)題.

2. 流量浪費(fèi).有客戶端不需要的對(duì)象被同步過(guò)來(lái)了.我們知道它是基于格子來(lái)管理地圖對(duì)象的.那么就會(huì)無(wú)法保證九宮區(qū)域一定剛好是視野范圍.肯定是大于視野區(qū)域這樣才保證同步對(duì)象正確.(如果是俯視角那種 ,視野就會(huì)是一個(gè) 梯形范圍.)

或者你可以在服務(wù)端中,根據(jù)客戶端梯形視野在作一遍初篩.

到此這篇關(guān)于Go語(yǔ)言實(shí)現(xiàn)AOI區(qū)域視野管理流程詳解的文章就介紹到這了,更多相關(guān)Go AOI區(qū)域視野管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mac GoLand打不開(kāi)(閃退)也不報(bào)錯(cuò)的解決方案

    Mac GoLand打不開(kāi)(閃退)也不報(bào)錯(cuò)的解決方案

    這篇文章主要介紹了Mac GoLand打不開(kāi)(閃退)也不報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Golang中interface轉(zhuǎn)string輸出打印方法

    Golang中interface轉(zhuǎn)string輸出打印方法

    這篇文章主要給大家介紹了關(guān)于Golang中interface轉(zhuǎn)string輸出打印的相關(guān)資料,在go語(yǔ)言中interface轉(zhuǎn)string可以直接使用fmt提供的fmt函數(shù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • Go語(yǔ)言開(kāi)發(fā)redis封裝及簡(jiǎn)單使用詳解

    Go語(yǔ)言開(kāi)發(fā)redis封裝及簡(jiǎn)單使用詳解

    這篇文章主要為大家介紹了Go語(yǔ)言開(kāi)發(fā)redis的封裝及簡(jiǎn)單使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-11-11
  • Golang在Window環(huán)境使用Imagick7的過(guò)程

    Golang在Window環(huán)境使用Imagick7的過(guò)程

    這篇文章主要介紹了Golang在Window環(huán)境使用Imagick7的過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Go語(yǔ)言對(duì)前端領(lǐng)域的入侵WebAssembly運(yùn)行原理

    Go語(yǔ)言對(duì)前端領(lǐng)域的入侵WebAssembly運(yùn)行原理

    這篇文章主要為大家介紹了不安分的Go語(yǔ)言對(duì)Web?前端領(lǐng)域的入侵WebAssembly運(yùn)行原理實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang 統(tǒng)計(jì)字符串字?jǐn)?shù)的方法示例

    Golang 統(tǒng)計(jì)字符串字?jǐn)?shù)的方法示例

    本篇文章主要介紹了Golang 統(tǒng)計(jì)字符串字?jǐn)?shù)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • golang中三種線程安全的MAP小結(jié)

    golang中三種線程安全的MAP小結(jié)

    在Go語(yǔ)言中,Map是并發(fā)不安全的,本文主要介紹了golang中三種線程安全的MAP小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Golang 中整數(shù)轉(zhuǎn)字符串的方法

    Golang 中整數(shù)轉(zhuǎn)字符串的方法

    這篇文章主要介紹了Golang 中整數(shù)轉(zhuǎn)字符串的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • Go?io/fs.FileMode文件系統(tǒng)基本操作和權(quán)限管理深入理解

    Go?io/fs.FileMode文件系統(tǒng)基本操作和權(quán)限管理深入理解

    這篇文章主要為大家介紹了Go?io/fs.FileMode文件系統(tǒng)基本操作和權(quán)限管理深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • GO語(yǔ)言基礎(chǔ)之?dāng)?shù)組

    GO語(yǔ)言基礎(chǔ)之?dāng)?shù)組

    或許您是從其他語(yǔ)言轉(zhuǎn)到GO語(yǔ)言這邊的,那麼在其他語(yǔ)言的影響下您可能會(huì)不太適應(yīng)GO語(yǔ)言的數(shù)組,因?yàn)镚O語(yǔ)言把數(shù)組給拆分成了array,slice和map,需要的朋友可以參考下
    2015-01-01

最新評(píng)論

襄樊市| 南安市| 阳泉市| 临沭县| 页游| 大连市| 同江市| 本溪市| 晋城| 绥芬河市| 奉贤区| 秦皇岛市| 佛教| 林甸县| 泽州县| 叶城县| 丽水市| 莫力| 磐安县| 天水市| 梅河口市| 通河县| 简阳市| 昌图县| 香格里拉县| 涡阳县| 高阳县| 沙河市| 天全县| 房产| 内黄县| 和林格尔县| 日土县| 保靖县| 迁安市| 宁南县| 车险| 泰来县| 石门县| 忻州市| 磐安县|