游戏的AOI算法应该算作游戏的基础核心了,许多逻辑都是因为AOI进出事件驱动的,许多网络同步数据也是因为AOI进出事件产生的。因此,良好的AOI算法和基于AOI算法的优化,是提高游戏性能的关键。
我在实践中所熟知的游戏AOI算法大致有两种,在此做一些总结,顺便梳理一下,打算设计出一套统一的接口封装不同的算法实现(网络上还有些其他算法,因为不熟悉不作记录了)。我所记录的这两种算法也算经典了,一个叫做网格法,一个叫做双链表法。具体细节可参考如下:https://zhuanlan.zhihu.com/p/201588990
根据 文章描述,自己动手初步实现了九宫格算法,该版本支持不同半径AOI。
grid.go代码如下:
package grid
import (
"fmt"
)
type void struct{}
func newVoid() void {
return struct{}{}
}
type Grid struct {
GridId int32
SceneId int32
Players map[int32]void
FollowerPlayers map[int32]void // 关注本格子的player, 本格子在该player视距之内, 因为视距不同,需要增加该变量缓存
}
func newGrid(GridId int32, SceneId int32) *Grid {
return &Grid{
GridId: GridId,
SceneId: SceneId,
Players: make(map[int32]void),
FollowerPlayers: make(map[int32]void),
}
}
type SceneAOIManager struct {
SceneId int32
Length int32
Width int32
Side int32
Grids map[int32]*Grid
}
func Ceil(a int32, b int32) int32 {
if a%b == 0 {
return a / b
}
return a/b + 1
}
func NewSceneAOIManager(SceneId int32, Length int32, Width int32, Side int32) *SceneAOIManager {
LengthNum := Ceil(Length, Side)
WidthNum := Ceil(Width, Side)
GridNum := WidthNum * LengthNum
Scene := &SceneAOIManager{
SceneId: SceneId,
Length: Length,
Width: Width,
Side: Side,
Grids: make(map[int32]*Grid),
}
var i int32
for i = 0; i < GridNum; i++ {
Scene.Grids[i] = newGrid(i, SceneId)
}
AllSceneManager[SceneId] = Scene
return Scene
}
func (Scene *SceneAOIManager) getGridId(x int32, y int32) int32 {
var GridId int32 = -1
XNum := Max(x-1, 0) / Scene.Side
YNum := Max(y-1, 0) / Scene.Side
XMax := Ceil(Scene.Length, Scene.Side)
GridId = XNum + YNum*XMax
return GridId
}
// Player所能看到的玩家
func (Scene *SceneAOIManager) PlayerNeighbor(Entity *EntityAOI) (map[int32]void, map[int32]void) {
Watching := make(map[int32]void)
GridId := Scene.getGridId(Entity.X, Entity.Y)
Range := Max(Entity.VisibleArea-1, 0)/Scene.Side + 1
AOIGrids := Scene.VisibleGridByRound(GridId, Range)
for GirdId := range AOIGrids {
GridObj :&#